圓環(huán)進(jìn)度動(dòng)畫

本篇文章主要記錄本周學(xué)習(xí)的進(jìn)度動(dòng)畫,一個(gè)特別有趣的例子,當(dāng)我們點(diǎn)擊的時(shí)候,會(huì)開始進(jìn)度值變化,還會(huì)有圓環(huán)進(jìn)度變化
演示:


image.png

下面讓我們一起實(shí)現(xiàn)一下

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>

布局可以根據(jù)自己需要調(diào)整,這里為了好看,我就設(shè)置了屏幕居中了
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 {

    /**
     * 圓環(huán)畫筆,繪制背景
     * */
    private val mBgPaint by lazy {
        Paint().apply {
            strokeWidth = mLineWidth
            color = mBgColor
            style = Paint.Style.STROKE
            isAntiAlias = true
            isDither = true
        }
    }

    /**
     * 背景顏色
     * */
    private var mBgColor = 0

    /**
     * 圓環(huán)畫筆,繪制進(jìn)度
     * */
    private val mPgPaint by lazy {
        Paint().apply {
            strokeWidth = mLineWidth
            color = mPgColor
            style = Paint.Style.STROKE
            isAntiAlias = true
            isDither = true
        }
    }


    /**
     * 進(jìn)度顏色
     * */
    private var mPgColor = 0

    /**
     * 線寬
     * */
    private var mLineWidth = dp2px(0)

    /**
     * 畫筆,繪制進(jìn)度值
     * */
    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

    /**
     * 控件中心點(diǎn)
     * */
    private var cx = 0f
    private var cy = 0f

    /**
     * 圓環(huán)半徑
     * */
    private var radius = 0f

    /**
     * 下載動(dòng)畫因子,也就是進(jìn)度值
     * */
    private var progress = 0f

    /**
     * 下載動(dòng)畫
     * */
    private var mAnimator: ValueAnimator? = null

    /**
     * 矩陣
     * */
    private val mRectF by lazy {
        RectF(cx - radius,cy - radius,cx + radius,cy + radius)
    }

    /**
     * 進(jìn)度值
     * */
    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)
        //繪制背景圓環(huán)
        canvas?.drawCircle(cx,cy,radius,mBgPaint)
        //繪制進(jìn)度圓環(huán)
        canvas?.drawArc(mRectF,0f,progress * 360,false,mPgPaint)
        //文字寬度的一半
        val mHalfWidth = mTxPaint.measureText(mText) / 2
        Log.v("pxd","mHalfWidth:$mHalfWidth")
        //繪制顯示的進(jìn)度值
        canvas?.drawText(mText,cx - mHalfWidth,cy - mFontMetricsInt.ascent / 2,mTxPaint)
    }

    /**
     * 下載,實(shí)現(xiàn)下載動(dòng)畫
     * */
    private fun initAnimator() {

        mAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
            duration = 2000
            addUpdateListener {
                progress = it.animatedValue as Float
                mText = "${(progress * 100).toInt()}%"
                //重繪
                invalidate()
            }
            start()
        }
    }

    /**
     * 開始動(dòng)畫或者暫停動(dòng)畫
     * */
    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 轉(zhuǎn) px
     * */
    private fun dp2px(dpValue: Int): Float {
        val scale = context.resources.displayMetrics.density
        return (dpValue * scale + 0.5f)
    }
}

這里有兩個(gè)需要提到的點(diǎn)

  • 1.如何提取自定義屬性
  • 2.如何繪制文本,屬性動(dòng)畫這里就不再提了

提取自定義屬性

第一步在values所對(duì)應(yīng)的包名下新建attrsxml文件
第二步聲明自己想要的屬性
第三步在構(gòu)造方法中提取屬性,也可以單獨(dú)寫一個(gè)方法來(lái)提取屬性,我這里就是
最后在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方法,用來(lái)提取屬性

/**
     * 提取自定義屬性
     * */
    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 指定從哪個(gè)樣式中解析
        * attrs: 從哪里解析 解析的數(shù)據(jù)在哪里
        * 返回值 TypedArray
        * 必須使用 recycle() 回收
        * */

最后在xml文件里使用屬性,自定義屬性一般以app:開頭

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

那么我們?nèi)绾卫L制文本呢,具體來(lái)說(shuō)就是調(diào)用canvas?.drawText方法
里面有4個(gè)參數(shù),第一個(gè)參數(shù)是繪制的內(nèi)容,中間兩個(gè)參數(shù)表示繪制的起始坐標(biāo),第4個(gè)參數(shù)畫筆,這里我們的x坐標(biāo)取繪制文字的起始點(diǎn)的橫坐標(biāo),也就是圓中心坐標(biāo)cx減去一半文字寬度的距離mHalfWidth,y坐標(biāo)取值文字高度的一半,這里求文字高度的方法不是唯一的,可以通過(guò)文字矩陣的高度的一半 - bottom的距離,這里通過(guò)將y坐標(biāo)向下平移一半的ascent的距離得到,ascent的值為負(fù),所有減去一半就是向下平移一半的距離cy - mFontMetricsInt.ascent / 2

//繪制顯示的進(jìn)度值
canvas?.drawText(mText,cx - mHalfWidth,cy - mFontMetricsInt.ascent / 2,mTxPaint)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容