Android自定义形状进度条实现方法咨询
Android自定义形状进度条实现方案
嘿,既然你已经搞定了圆形、环形和条形进度条,那自定义形状的进度条对你来说只是换个“壳子”的事儿~下面给你几个实用的实现思路,从灵活度高到快速实现的都有:
一、自定义View(最灵活,适合任意复杂形状)
这是最万能的方案,完全靠你用Canvas和Path来绘制想要的形状。步骤大概是这样:
- 定义自定义属性:在
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>
- 自定义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() } }
- 布局中使用:直接在xml里引用这个自定义View,配置属性即可。
二、VectorDrawable + 属性动画(快速实现简单形状)
如果你的自定义形状是规则的SVG图形,可以转成VectorDrawable,用动画控制进度,不用写太多代码:
- 创建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>
- 启动进度动画:在代码里用
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控制进度:
- 创建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>
- 更新进度:
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
相关产品推荐
相关产品推荐

