circle_progress.gif
圆形进度条分析:
- 圆
- 圆弧
- 进度值
所以这里我们先用
drawCircle
画出圆,再使用drawArc
画出圆弧(这里的圆弧要画整个圆,0°~360°),进度值使用drawText
画出来,x
起点值和baseline
的值同之前的算法一致。
完整代码
代码语言:javascript复制class CircleProgressbar @JvmOverloads constructor(
context: Context,
attributeSet: AttributeSet,
defStyle: Int = 0
) : View(context, attributeSet, defStyle) {
private val mOuterPaint = Paint()
private val mInnerPaint = Paint()
private val mTextPaint = TextPaint()
private var mTextColor = Color.BLACK
private var mTextSize = 15
private var mOutColor = Color.BLUE
private var mInnerColor = Color.RED
private var mProgressWidth = dp2Px(6, context.resources)
private var mRadiusWidth = dp2Px(40,context.resources).toFloat()
private var mSweepRadius = 0
private lateinit var valueAnim:ValueAnimator
private var mRectF = Rect()
//初始化相关属性
init {
val ta = context.obtainStyledAttributes(attributeSet,
R.styleable.CircleProgressbar
)
mOutColor = ta.getColor(R.styleable.CircleProgressbar_outerColor, mOutColor)
mInnerColor = ta.getColor(R.styleable.CircleProgressbar_innerColor, mInnerColor)
mTextColor = ta.getColor(R.styleable.CircleProgressbar_customTextColor, mTextColor)
mTextSize = ta.getDimensionPixelSize(
R.styleable.CircleProgressbar_customTextSize,
sp2Px(mTextSize, context.resources)
)
mProgressWidth = ta.getInt(R.styleable.CircleProgressbar_progressWidth, mProgressWidth)
mRadiusWidth = ta.getDimension(
R.styleable.CircleProgressbar_radiusWidth,
dp2Px(mRadiusWidth.toInt(),context.resources).toFloat()
)
ta.recycle()
getPaintByParams(mOuterPaint, mOutColor)
getPaintByParams(mInnerPaint, mInnerColor)
getTextPaintByParams()
}
private fun getTextPaintByParams() {
mTextPaint.color = mTextColor
mTextPaint.textSize = mTextSize.toFloat()
mTextPaint.isAntiAlias = true
mTextPaint.isDither = true
}
private fun getPaintByParams(paint: Paint, color: Int) {
paint.color = color
paint.isAntiAlias = true
paint.strokeWidth = mProgressWidth.toFloat()
paint.style = Paint.Style.STROKE
}
//测量
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)
//画圆
canvas.drawCircle((width/2).toFloat(), (height/2).toFloat(),
mRadiusWidth-mProgressWidth/2,mOuterPaint)
val rectF = RectF()
rectF.left = (mProgressWidth/2).toFloat()
rectF.top = (mProgressWidth/2).toFloat()
rectF.right = width.toFloat()-mProgressWidth/2
rectF.bottom = height.toFloat() - mProgressWidth/2
//画圆弧
canvas.drawArc(rectF, 0F, mSweepRadius.toFloat(),false,mInnerPaint)
mTextPaint.getTextBounds(mSweepRadius.toString(),0,mSweepRadius.toString().length,mRectF)
val fontMetricsInt = mTextPaint.fontMetricsInt
//计算文字的 baseline
val dy = (fontMetricsInt.bottom-fontMetricsInt.top)/2-fontMetricsInt.bottom height/2
val x = mRadiusWidth-mRectF.width()/2
//画文字
canvas.drawText(mSweepRadius.toString(), x, dy.toFloat(),mTextPaint)
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
startAnim()
}
//开始动画
private fun startAnim(){
valueAnim = ValueAnimator.ofInt(0,360)
valueAnim.duration = 4000
valueAnim.addUpdateListener {
val value = it.animatedValue as Int
mSweepRadius = value
invalidate()
}
valueAnim.start()
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
stopAnim()
}
//结束动画
private fun stopAnim(){
valueAnim.apply {
cancel()
removeAllUpdateListeners()
}
}
}
项目地址