美文网首页
自定义 View 实战 02 - 圆弧形进度条

自定义 View 实战 02 - 圆弧形进度条

作者: Kotyo | 来源:发表于2020-06-16 14:31 被阅读0次
    效果图
    分析动图可以知道,动画主要由三部分组成:
    • 内圆弧
    • 外圆弧
    • 中间文字

    实现过程及注释

    自定义属性

    <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>
    

    具体实现

    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
        }
    

    调用

            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 的实现过程及关键注释都比较易于理解

    相关文章

      网友评论

          本文标题:自定义 View 实战 02 - 圆弧形进度条

          本文链接:https://www.haomeiwen.com/subject/dbalxktx.html