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

Android自定义形状进度条实现方法咨询

Android自定义形状进度条实现方案

嘿,既然你已经搞定了圆形、环形和条形进度条,那自定义形状的进度条对你来说只是换个“壳子”的事儿~下面给你几个实用的实现思路,从灵活度高到快速实现的都有:

一、自定义View(最灵活,适合任意复杂形状)

这是最万能的方案,完全靠你用Canvas和Path来绘制想要的形状。步骤大概是这样:

  1. 定义自定义属性:在res/values/attrs.xml里添加进度相关的属性,方便在布局里配置:
<declare-styleable name="CustomShapeProgressBar">
    <attr name="progress" format="integer" />
    <attr name="maxProgress" format="integer" />
    <attr name="bgColor" format="color" />
    <attr name="progressColor" format="color" />
</declare-styleable>
  1. 自定义View类:继承View,解析属性后重写onMeasure和onDraw完成绘制逻辑:
class CustomShapeProgressBar @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    private var progress = 0
    private var maxProgress = 100
    private val bgPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val progressPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val shapePath = Path()

    init {
        attrs?.let {
            val ta = context.obtainStyledAttributes(it, R.styleable.CustomShapeProgressBar)
            progress = ta.getInt(R.styleable.CustomShapeProgressBar_progress, 0)
            maxProgress = ta.getInt(R.styleable.CustomShapeProgressBar_maxProgress, 100)
            bgPaint.color = ta.getColor(R.styleable.CustomShapeProgressBar_bgColor, Color.GRAY)
            progressPaint.color = ta.getColor(R.styleable.CustomShapeProgressBar_progressColor, Color.BLUE)
            ta.recycle()
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        // 处理wrap_content的默认尺寸
        val defaultSize = dp2px(100)
        val width = resolveSize(defaultSize, widthMeasureSpec)
        val height = resolveSize(defaultSize, heightMeasureSpec)
        setMeasuredDimension(width, height)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val width = width.toFloat()
        val height = height.toFloat()

        // 1. 绘制自定义形状的背景(这里以心形为例)
        shapePath.reset()
        shapePath.moveTo(width/2, height*0.3f)
        shapePath.cubicTo(width*0.1f, 0f, 0f, height*0.4f, width/2, height*0.8f)
        shapePath.cubicTo(width, height*0.4f, width*0.9f, 0f, width/2, height*0.3f)
        canvas.drawPath(shapePath, bgPaint)

        // 2. 绘制进度填充:用画布裁剪控制显示范围
        val progressRatio = progress.toFloat() / maxProgress.toFloat()
        canvas.save()
        // 这里是横向裁剪,你可以根据形状改成竖向、扇形等逻辑
        val clipRight = width * progressRatio
        canvas.clipRect(0f, 0f, clipRight, height)
        canvas.drawPath(shapePath, progressPaint)
        canvas.restore()
    }

    // 对外提供更新进度的方法
    fun setProgress(newProgress: Int) {
        progress = newProgress.coerceIn(0, maxProgress)
        invalidate()
    }

    private fun dp2px(dp: Int): Int {
        return (dp * resources.displayMetrics.density).toInt()
    }
}
  1. 布局中使用:直接在xml里引用这个自定义View,配置属性即可。

二、VectorDrawable + 属性动画(快速实现简单形状)

如果你的自定义形状是规则的SVG图形,可以转成VectorDrawable,用动画控制进度,不用写太多代码:

  1. 创建VectorDrawable:把形状分成背景层和进度层,用trimPathEnd控制进度显示:
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="200dp"
    android:height="200dp"
    android:viewportWidth="200"
    android:viewportHeight="200">

    <!-- 背景形状 -->
    <path
        android:fillColor="#E0E0E0"
        android:pathData="M100,60 C20,0 0,80 100,160 C200,80 180,0 100,60" />

    <!-- 进度形状,trimPathEnd从1到0表示进度从0到100% -->
    <path
        android:fillColor="#2196F3"
        android:pathData="M100,60 C20,0 0,80 100,160 C200,80 180,0 100,60"
        android:trimPathEnd="1" />
</vector>
  1. 启动进度动画:在代码里用ObjectAnimator改变trimPathEnd的值:
val progressDrawable = imageView.drawable as? AnimatedVectorDrawable
val animator = ObjectAnimator.ofFloat(progressDrawable, "trimPathEnd", 1f, 0f)
animator.duration = 2000 // 动画时长2秒
animator.start()

三、LayerDrawable组合(适合已有形状资源)

如果已经有自定义形状的背景图和进度图,用LayerDrawable叠起来,通过level控制进度:

  1. 创建layer_list.xml:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 背景层 -->
    <item android:drawable="@drawable/custom_shape_bg" />
    <!-- 进度层,用clip控制显示范围 -->
    <item>
        <clip
            android:clipOrientation="horizontal"
            android:gravity="left">
            <bitmap android:src="@drawable/custom_shape_progress" />
        </clip>
    </item>
</layer-list>
  1. 更新进度:
val layerDrawable = imageView.drawable as LayerDrawable
val progress = 60 // 0-100的进度值
layerDrawable.setLevel(progress * 100) // level范围是0-10000

一些小Tips

  • 复杂形状的精准填充可以用Region或者Path.op()做路径运算,实现不规则区域的进度显示。
  • 自定义View要处理好onMeasure,避免wrap_content时显示异常。
  • 性能优化:Paint、Path等对象要放在成员变量里,不要在onDraw里重复创建;开启Paint的抗锯齿提升显示效果。

内容的提问来源于stack exchange,提问作者jNewbie

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:37:02