效果圖
分析動(dòng)圖可以知道,動(dòng)畫主要由三部分組成:
- 內(nèi)圓弧
- 外圓弧
- 中間文字
實(shí)現(xiàn)過程及注釋
自定義屬性
<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>
具體實(shí)現(xiàn)
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()
}
//測(cè)量
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
//這里需要使用 measureWidth 和 measureHeight , 如果使用 width 和 height 不一定有值
var widthSize = measuredWidth
var heightSize = measuredHeight
//保證是一個(gè)正方形
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
//如果不減去 畫筆的寬度,畫出來的圖形會(huì)顯示不全
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
//用于測(cè)量基線
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
}
調(diào)用
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()
自定義圓弧就這樣實(shí)現(xiàn)出來了,從 0 到 1 的實(shí)現(xiàn)過程及關(guān)鍵注釋都比較易于理解