美文网首页
圆环进度动画

圆环进度动画

作者: 爱写代码的小王子 | 来源:发表于2020-10-15 13:15 被阅读0次

    本篇文章主要记录本周学习的进度动画,一个特别有趣的例子,当我们点击的时候,会开始进度值变化,还会有圆环进度变化
    演示:


    image.png

    下面让我们一起实现一下

    activity_mian界面布局

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <me.jrl.demo4.DownloadView
            android:id="@+id/mDownLoadView"
            android:layout_width="200dp"
            android:layout_height="200dp"
            app:bgColor="@color/colorAccent"
            app:pgColor="@color/colorPrimary"
            app:txColor="@android:color/black"
            app:lineWidth="20dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    布局可以根据自己需要调整,这里为了好看,我就设置了屏幕居中了
    DownloadView自定义View

    import android.animation.ValueAnimator
    import android.content.Context
    import android.graphics.Canvas
    import android.graphics.Color
    import android.graphics.Paint
    import android.graphics.RectF
    import android.text.TextPaint
    import android.util.AttributeSet
    import android.util.Log
    import android.view.MotionEvent
    import android.view.View
    import kotlin.math.log
    import kotlin.math.min
    
    class DownloadView : View {
    
        /**
         * 圆环画笔,绘制背景
         * */
        private val mBgPaint by lazy {
            Paint().apply {
                strokeWidth = mLineWidth
                color = mBgColor
                style = Paint.Style.STROKE
                isAntiAlias = true
                isDither = true
            }
        }
    
        /**
         * 背景颜色
         * */
        private var mBgColor = 0
    
        /**
         * 圆环画笔,绘制进度
         * */
        private val mPgPaint by lazy {
            Paint().apply {
                strokeWidth = mLineWidth
                color = mPgColor
                style = Paint.Style.STROKE
                isAntiAlias = true
                isDither = true
            }
        }
    
    
        /**
         * 进度颜色
         * */
        private var mPgColor = 0
    
        /**
         * 线宽
         * */
        private var mLineWidth = dp2px(0)
    
        /**
         * 画笔,绘制进度值
         * */
        private val mTxPaint by lazy {
            TextPaint().apply {
                strokeWidth = dp2px(2)
                textSize = dp2px(20)
                color = mTxColor
                style = Paint.Style.STROKE
                isAntiAlias = true
                isDither = true
            }
        }
    
        /**
         * 文本颜色
         * */
        private var mTxColor = 0
    
        /**
         * 控件中心点
         * */
        private var cx = 0f
        private var cy = 0f
    
        /**
         * 圆环半径
         * */
        private var radius = 0f
    
        /**
         * 下载动画因子,也就是进度值
         * */
        private var progress = 0f
    
        /**
         * 下载动画
         * */
        private var mAnimator: ValueAnimator? = null
    
        /**
         * 矩阵
         * */
        private val mRectF by lazy {
            RectF(cx - radius,cy - radius,cx + radius,cy + radius)
        }
    
        /**
         * 进度值
         * */
        private var mText = "0%"
    
        /**
         * 文字矩阵
         * */
        private val mFontMetricsInt by lazy {
            mTxPaint.fontMetricsInt
        }
    
        constructor(context: Context) : super(context) {}
    
        constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
            init(context, attrs)
        }
    
    
        /**
         * 提取自定义属性
         * */
        private fun init(context: Context, attrs: AttributeSet?) {
    
            val array = context.obtainStyledAttributes(attrs, R.styleable.DownloadView)
            mBgColor = array.getColor(R.styleable.DownloadView_bgColor, Color.GREEN)
            mPgColor = array.getColor(R.styleable.DownloadView_pgColor, Color.BLUE)
            mTxColor = array.getColor(R.styleable.DownloadView_txColor, Color.BLACK)
            mLineWidth = array.getDimension(R.styleable.DownloadView_lineWidth, dp2px(20))
            array.recycle()
    
        }
    
        override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
            super.onSizeChanged(w, h, oldw, oldh)
            cx = measuredWidth / 2f
            cy = measuredHeight / 2f
            //半径取宽高的最小值的一半,再减去线的宽度
            radius = min(measuredWidth, measuredHeight) / 2f - mLineWidth
        }
    
        override fun onDraw(canvas: Canvas?) {
            super.onDraw(canvas)
            //绘制背景圆环
            canvas?.drawCircle(cx,cy,radius,mBgPaint)
            //绘制进度圆环
            canvas?.drawArc(mRectF,0f,progress * 360,false,mPgPaint)
            //文字宽度的一半
            val mHalfWidth = mTxPaint.measureText(mText) / 2
            Log.v("pxd","mHalfWidth:$mHalfWidth")
            //绘制显示的进度值
            canvas?.drawText(mText,cx - mHalfWidth,cy - mFontMetricsInt.ascent / 2,mTxPaint)
        }
    
        /**
         * 下载,实现下载动画
         * */
        private fun initAnimator() {
    
            mAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
                duration = 2000
                addUpdateListener {
                    progress = it.animatedValue as Float
                    mText = "${(progress * 100).toInt()}%"
                    //重绘
                    invalidate()
                }
                start()
            }
        }
    
        /**
         * 开始动画或者暂停动画
         * */
        fun start() {
            if (mAnimator != null) {
                if (mAnimator!!.isPaused) {
                    mAnimator!!.resume()
                } else {
                    mAnimator!!.start()
                }
            } else {
                initAnimator()
            }
        }
    
        override fun onTouchEvent(event: MotionEvent?): Boolean {
            if (event?.action == MotionEvent.ACTION_DOWN){
                start()
            }
            return true
        }
    
        /**
         * dp 转 px
         * */
        private fun dp2px(dpValue: Int): Float {
            val scale = context.resources.displayMetrics.density
            return (dpValue * scale + 0.5f)
        }
    }
    

    这里有两个需要提到的点

    • 1.如何提取自定义属性
    • 2.如何绘制文本,属性动画这里就不再提了

    提取自定义属性

    第一步在values所对应的包名下新建attrsxml文件
    第二步声明自己想要的属性
    第三步在构造方法中提取属性,也可以单独写一个方法来提取属性,我这里就是
    最后在xml文件里引用自定义的属性

    atrrsxml文件

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="ProgressView">
            <attr name="bg_color" format="color|integer"/>
            <attr name="pg_color" format="color|integer"/>
            <attr name="tv_color" format="color|integer"/>
    
            <attr name="lineWidth" format="float|dimension"/>
            <attr name="textSize" format="float|dimension"/>
        </declare-styleable>
    </resources>
    

    init方法,用来提取属性

    /**
         * 提取自定义属性
         * */
        private fun init(context: Context, attrs: AttributeSet?) {
    
            val array = context.obtainStyledAttributes(attrs, R.styleable.DownloadView)
            mBgColor = array.getColor(R.styleable.DownloadView_bgColor, Color.GREEN)
            mPgColor = array.getColor(R.styleable.DownloadView_pgColor, Color.BLUE)
            mTxColor = array.getColor(R.styleable.DownloadView_txColor, Color.BLACK)
            mLineWidth = array.getDimension(R.styleable.DownloadView_lineWidth, dp2px(20))
            array.recycle()
    
        }
    

    这里需要注意的是

            /**
            * 1.使用obtainStyledAttributes
            * resId: R.styleable.ProgressView 指定从哪个样式中解析
            * attrs: 从哪里解析 解析的数据在哪里
            * 返回值 TypedArray
            * 必须使用 recycle() 回收
            * */
    

    最后在xml文件里使用属性,自定义属性一般以app:开头

            app:bgColor="@color/colorAccent"
            app:pgColor="@color/colorPrimary"
            app:txColor="@android:color/black"
            app:lineWidth="20dp"
    

    那么我们如何绘制文本呢,具体来说就是调用canvas?.drawText方法
    里面有4个参数,第一个参数是绘制的内容,中间两个参数表示绘制的起始坐标,第4个参数画笔,这里我们的x坐标取绘制文字的起始点的横坐标,也就是圆中心坐标cx减去一半文字宽度的距离mHalfWidth,y坐标取值文字高度的一半,这里求文字高度的方法不是唯一的,可以通过文字矩阵的高度的一半 - bottom的距离,这里通过将y坐标向下平移一半的ascent的距离得到,ascent的值为负,所有减去一半就是向下平移一半的距离cy - mFontMetricsInt.ascent / 2

    //绘制显示的进度值
    canvas?.drawText(mText,cx - mHalfWidth,cy - mFontMetricsInt.ascent / 2,mTxPaint)
    

    相关文章

      网友评论

          本文标题:圆环进度动画

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