效果图
分析动图可以知道,动画主要由三部分组成:
- 内圆弧
- 外圆弧
- 中间文字
实现过程及注释
自定义属性
代码语言:javascript复制<declare-styleable name="ArcProgressbar">
<attr name="progressWidth" format="integer" />
<attr name="outerColor" format="reference|color" />
<attr name="innerColor" format="reference|color" />
<attr name="progressText" format="string" />
<attr name="customTextSize"/>
<attr name="progressTextColor" format="reference|color" />
</declare-styleable>
具体实现
代码语言:javascript复制class ArcProgressbar @JvmOverloads constructor(
context: Context,
attributeSet: AttributeSet?,
defStyle: Int = 0
) : View(context, attributeSet, defStyle) {
private var mOuterPaint = Paint()
private var mInnerPaint = Paint()
private var mTextPaint = TextPaint()
private var mOuterColor = Color.BLACK
private var mInnerColor = Color.BLUE
private var mProgressWidth = 5
private var mText = ""
private var mTextColor = Color.BLACK
private var mCurProgress = 0
private var mMaxProgress = 0
private var mTextSize = 15
private val rectF = RectF()
private val rect = Rect()
private val startAngle = 135f
private var sweepAngle = 270f
init {
//自定义属性获取
val ta = context.obtainStyledAttributes(attributeSet, R.styleable.ArcProgressbar)
mOuterColor = ta.getColor(R.styleable.ArcProgressbar_outerColor, mOuterColor)
mInnerColor = ta.getColor(R.styleable.ArcProgressbar_innerColor, mInnerColor)
mProgressWidth =
ta.getInt(R.styleable.ArcProgressbar_progressWidth, dp2Px(mProgressWidth, resources))
mText = ta.getString(R.styleable.ArcProgressbar_progressText).toString()
mTextColor = ta.getColor(R.styleable.ArcProgressbar_progressTextColor, mTextColor)
mTextSize = ta.getDimensionPixelSize(
R.styleable.ArcProgressbar_customTextSize,
sp2Px(mTextSize, resources)
)
ta.recycle()
//各种 Paint 初始化
mOuterPaint.isAntiAlias = true
mOuterPaint.color = mOuterColor
mOuterPaint.style = Paint.Style.STROKE
mOuterPaint.strokeWidth = mProgressWidth.toFloat()
mInnerPaint.isAntiAlias = true
mInnerPaint.color = mInnerColor
mInnerPaint.style = Paint.Style.STROKE
mInnerPaint.strokeWidth = mProgressWidth.toFloat()
mTextPaint.isAntiAlias = true
mTextPaint.color = mTextColor
mTextPaint.textSize = mTextSize.toFloat()
mTextPaint.strokeWidth = mProgressWidth.toFloat()
}
//测量
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
//这里需要使用 measureWidth 和 measureHeight , 如果使用 width 和 height 不一定有值
var widthSize = measuredWidth
var heightSize = measuredHeight
//保证是一个正方形
if (widthSize > heightSize) widthSize = heightSize
if (heightSize > widthSize) heightSize = widthSize
setMeasuredDimension(widthSize, heightSize)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
//画外圆
val left = mProgressWidth / 2
val top = mProgressWidth / 2
//如果不减去 画笔的宽度,画出来的图形会显示不全
val right = width - mProgressWidth / 2
val bottom = height - mProgressWidth / 2
rectF.left = left.toFloat()
rectF.top = top.toFloat()
rectF.right = right.toFloat()
rectF.bottom = bottom.toFloat()
canvas.drawArc(rectF, startAngle, sweepAngle, false, mOuterPaint)
if (mMaxProgress == 0) return
//画外圆
val percent = mCurProgress / mMaxProgress.toFloat()
sweepAngle *= percent
canvas.drawArc(rectF, startAngle, sweepAngle, false, mInnerPaint)
//画文字
mTextPaint.getTextBounds(mText, 0, mText.length, rect)
val startX = (height / 2 - mProgressWidth) - rect.width() / 2
val fontMetricsInt = mTextPaint.fontMetricsInt
//用于测量基线
val vy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom
val baseLine = height / 2 vy
canvas.drawText(sweepAngle.toInt().toString(), startX.toFloat(), baseLine.toFloat(), mTextPaint)
}
fun setCurProgress(progress: Int) {
mCurProgress = progress
invalidate()
}
fun setMaxProgress(progress: Int) {
mMaxProgress = progress
}
调用
代码语言:javascript复制 val maxProgress = 3000
val valueAnimator = ValueAnimator.ofInt(0,2500)
arc_progressbar.setMaxProgress(maxProgress)
valueAnimator.interpolator = AccelerateInterpolator()
valueAnimator.duration = 2500
valueAnimator.addUpdateListener {
val value = it.animatedValue as Int
arc_progressbar.setCurProgress(value)
}
valueAnimator.start()
自定义圆弧就这样实现出来了,从 0 到 1 的实现过程及关键注释都比较易于理解