Android中如何实现带左右切角的ProgressBar/SeekBar布局
Android实现双侧切角样式进度条/拖动条
别死磕系统ProgressBar/SeekBar默认的progressDrawable属性,系统自带的进度绘制逻辑只支持常规圆角,改不出你要的双侧斜切效果,直接自定义Drawable画形状是最省事的方案,ProgressBar和SeekBar的轨道逻辑通用,SeekBar只需要额外处理滑块样式就行。
核心实现思路
- 废弃系统默认的进度条绘制资源,自己用Path画出带左右斜切的轨道形状
- 分层绘制:先画全宽的切角背景轨道,再画对应进度长度的切角填充区域
- 进度更新时直接刷新Drawable的绘制路径,不用重写整个控件
具体代码实现
首先写自定义切角进度Drawable:
class CutCornerProgressDrawable( private val trackColor: Int, private val progressColor: Int, private val cutSize: Float // 切角的横向偏移长度,直接对应切角大小 ) : Drawable() { private val trackPaint = Paint(Paint.ANTI_ALIAS_FLAG) private val progressPaint = Paint(Paint.ANTI_ALIAS_FLAG) private var progressRatio = 0f init { trackPaint.color = trackColor progressPaint.color = progressColor } fun updateProgress(ratio: Float) { progressRatio = ratio.coerceIn(0f, 1f) invalidateSelf() } override fun draw(canvas: Canvas) { val drawWidth = bounds.width().toFloat() val drawHeight = bounds.height().toFloat() // 绘制带双侧切角的背景轨道 val trackPath = Path().apply { moveTo(cutSize, 0f) lineTo(drawWidth - cutSize, 0f) lineTo(drawWidth, drawHeight) lineTo(0f, drawHeight) close() } canvas.drawPath(trackPath, trackPaint) // 绘制当前进度填充,进度拉满时自动对齐右侧切角 val currentProgressWidth = drawWidth * progressRatio val progressPath = Path().apply { moveTo(cutSize, 0f) if (currentProgressWidth > cutSize) { lineTo(currentProgressWidth, 0f) lineTo(currentProgressWidth, drawHeight) } lineTo(0f, drawHeight) close() } canvas.drawPath(progressPath, progressPaint) } override fun setAlpha(alpha: Int) { trackPaint.alpha = alpha progressPaint.alpha = alpha invalidateSelf() } override fun setColorFilter(colorFilter: ColorFilter?) { trackPaint.colorFilter = colorFilter progressPaint.colorFilter = colorFilter invalidateSelf() } override fun getOpacity(): Int = PixelFormat.TRANSLUCENT }
控件使用方式
- ProgressBar场景
布局里把系统默认进度样式置空,避免层级冲突:
代码里绑定Drawable,监听进度更新:<ProgressBar android:id="@+id/pb_cut_corner" android:layout_width="match_parent" android:layout_height="24dp" android:max="100" android:progressDrawable="@null"/>val cutDrawable = CutCornerProgressDrawable( trackColor = Color.LTGRAY, progressColor = Color.BLUE, cutSize = 12f // 按设计稿尺寸调整,不要超过控件高度的1/2,否则形状会错位 ) binding.pbCutCorner.apply { background = cutDrawable // 监听进度变化刷新Drawable setOnProgressChangeListener { _, progress, _ -> cutDrawable.updateProgress(progress / 100f) } // 设置初始进度 progress = 45 cutDrawable.updateProgress(0.45f) } - SeekBar场景
轨道部分逻辑和ProgressBar完全一致,布局里额外把默认tickMark、thumb样式按需替换就行,记得把默认progressDrawable置空,避免系统默认样式盖在自定义内容上面。如果需要滑块样式和切角匹配,自己做对应形状的thumb资源即可。
可选扩展
- 要做进度动画的话,直接给progressRatio加ValueAnimator,动画过程中调用
updateProgress()刷新就行,不用额外写动画逻辑 - 如果需要切角+圆角的组合效果,直接修改Path的轮廓,给边角加圆弧段即可
目标参考效果:
内容的提问来源于stack exchange,提问作者Adnan Mahida
相关产品推荐
相关产品推荐

