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

下面讓我們一起實(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)的包名下新建attrs的xml文件
第二步聲明自己想要的屬性
第三步在構(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)