You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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场景
    布局里把系统默认进度样式置空,避免层级冲突:
    <ProgressBar
        android:id="@+id/pb_cut_corner"
        android:layout_width="match_parent"
        android:layout_height="24dp"
        android:max="100"
        android:progressDrawable="@null"/>
    
    代码里绑定Drawable,监听进度更新:
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 09:24:16