Android開發(fā)Kotlin實現(xiàn)圓弧計步器示例詳解
效果圖

定義控件的樣式
看完效果后,我們先定義控件的樣式
<!-- 自定義View的名字 StepView -->
<!-- name 屬性名稱 format 格式
string 文字 color 顏色 dimension 字體大小
integer 數(shù)字 reference 資源或者顏色
-->
<declare-styleable name="StepView">
<attr name="borderWidth" format="dimension" />
<attr name="outColor" format="color" />
<attr name="innerColor" format="color" />
<attr name="unit" format="string" />
<attr name="currentStep" format="integer" />
<attr name="maxStep" format="integer" />
</declare-styleable>
自定義StepView
接下來我們自定義一個StepView(記步的View)
package cn.wwj.customview.widget
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import android.util.AttributeSet
import android.util.Log
import android.util.TypedValue
import android.view.animation.AccelerateInterpolator
import androidx.appcompat.widget.AppCompatTextView
import cn.wwj.customview.R
class StepView : AppCompatTextView {
/**
* 當(dāng)前走了多少步
*/
private var mCurrentStep: Int = 0
/**
* 最大走多少步,比如兩萬步 20000步
* 默認(rèn)100步,有個成語50步笑100步
*/
private var mMaxStep: Int = 100
/**
* 單位:步 %等
*/
private var mUnit: String?
/**
* 設(shè)置圓弧的邊框線寬度
*/
private var mBorderWidth: Float = dp2px(6F)
/**
* 設(shè)置外部圓弧的顏色
*/
private var mOuterColor: Int = resources.getColor(android.R.color.holo_blue_light)
/**
* 設(shè)置內(nèi)部圓弧的顏色
*/
private var mInnerColor: Int = resources.getColor(android.R.color.holo_red_light)
/**
* 圓弧畫筆
*/
private var mArcPaint: Paint = Paint()
/**
* 文本畫筆,用于繪畫走了多少步
*/
private var mTextPaint: Paint = Paint()
/**
* 日志過濾標(biāo)簽
*/
private val TAG = javaClass.simpleName
/**
* 圓弧起始角度
*/
private var mStartAngle = 135F
/**
* 圓弧從起始角度開始,掃描過的角度
*/
private var mSweepAngle = mStartAngle * 2
/**
* 比如用于獲取一個圓弧的矩形,onDraw()方法會調(diào)用多次,不必每次都創(chuàng)建Rect()對象
*/
private val mArcRect = RectF()
/**
* 比如用于獲取文字的大小,onDraw()方法會調(diào)用多次,不必每次都創(chuàng)建Rect()對象
* 用同一個對象即可
*/
private val mTextRect = Rect()
/**
* 值動畫師
*/
private var valueAnimator: ValueAnimator? = null
/**
* 最大進度
*/
private val MAX_PROGRESS = 100F
constructor(context: Context)
: this(context, null)
constructor(context: Context, attrs: AttributeSet?)
: this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int)
: super(context, attrs, defStyleAttr) {
val appearance = context.obtainStyledAttributes(attrs, R.styleable.StepView)
mBorderWidth = appearance.getDimension(R.styleable.StepView_borderWidth, mBorderWidth)
mOuterColor = appearance.getColor(R.styleable.StepView_outColor, mOuterColor)
mInnerColor = appearance.getColor(R.styleable.StepView_innerColor, mInnerColor)
mUnit = appearance.getString(R.styleable.StepView_unit)
mCurrentStep = appearance.getInt(R.styleable.StepView_currentStep, 0)
mMaxStep = appearance.getInt(R.styleable.StepView_maxStep, mMaxStep)
appearance.recycle()
setPaint()
}
/**
* 設(shè)置 圓弧畫筆用于繪制圓弧 和 文本畫筆,用于繪畫走了多少步
*/
private fun setPaint() {
// 畫筆的顏色
mArcPaint.color = mOuterColor
// 抗抖動
mArcPaint.isDither = true
// 抗鋸齒
mArcPaint.isAntiAlias = true
// 畫筆的樣式描邊,筆劃突出為半圓
mArcPaint.style = Paint.Style.STROKE
// 設(shè)置描邊的線帽樣式
mArcPaint.strokeCap = Paint.Cap.ROUND
// 設(shè)置描邊的寬度
mArcPaint.strokeWidth = mBorderWidth
// 畫筆的顏色
mTextPaint.color = currentTextColor
// 抗抖動
mTextPaint.isDither = true
// 抗鋸齒
mTextPaint.isAntiAlias = true
// 畫筆的樣式描邊,筆劃突出為半圓
mTextPaint.style = Paint.Style.FILL
// 設(shè)置描邊的線帽樣式
mTextPaint.strokeCap = Paint.Cap.ROUND
// 設(shè)置文本大小
mTextPaint.textSize = textSize
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
/**
* 獲取控件的寬高
*/
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
/**
* 如果寬度大于高度,取高度
* 否則取寬度
*/
val result = if (widthSize > heightSize) {
heightSize
} else {
widthSize
}
/**
* 設(shè)置控件的寬高
*/
setMeasuredDimension(result, result)
}
override fun onDraw(canvas: Canvas?) {
// 將矩形設(shè)置為 (0,0,0,0)
mArcRect.setEmpty()
mTextRect.setEmpty()
// 圓弧矩形左邊距,頂邊距,右邊距,底邊距
val left = mBorderWidth / 2
val top = mBorderWidth / 2
val right = width - mBorderWidth / 2
val bottom = height - mBorderWidth / 2
mArcRect.set(left, top, right, bottom)
// 繪制外部圓弧
mArcPaint.color = mOuterColor
canvas?.drawArc(mArcRect, mStartAngle, mSweepAngle, false, mArcPaint)
// 繪制內(nèi)部部圓弧
mArcPaint.color = mInnerColor
val sweepAngle = mCurrentStep * 1F / mMaxStep * mSweepAngle
canvas?.drawArc(mArcRect, mStartAngle, sweepAngle, false, mArcPaint)
val stepText = if (mUnit != null) {
"$mCurrentStep $mUnit"
} else {
mCurrentStep.toString()
}
// 獲取文本的寬高
mTextPaint.getTextBounds(stepText, 0, stepText.length, mTextRect)
val textX = width / 2F - mTextRect.width() / 2
val textY = height / 2F + getBaseline(mTextPaint)
// 繪制文本,第二個參數(shù)文本的起始索引,第三個參數(shù)要繪制的文字長度
// 開始繪制文字的x 坐標(biāo) y 坐標(biāo)
canvas?.drawText(stepText, 0, stepText.length, textX, textY, mTextPaint)
}
/**
* @param progress 進入0-100 之間
* @param duration 動畫時長,默認(rèn) 350毫秒
*/
fun setProgress(progress: Int, duration: Long = 350) {
valueAnimator?.cancel()
valueAnimator = null
val step = (progress / MAX_PROGRESS * mMaxStep).toInt()
valueAnimator = ValueAnimator.ofInt(mCurrentStep, step.coerceAtMost(mMaxStep))
valueAnimator?.duration = duration
valueAnimator?.interpolator = AccelerateInterpolator()
valueAnimator?.addUpdateListener {
mCurrentStep = it.animatedValue as Int
Log.d(TAG, "------$mCurrentStep")
invalidate()
}
valueAnimator?.startDelay = 10
valueAnimator?.start()
}
/**
* @param maxStep 最多走多少步,比如2000步
* @param duration 默認(rèn)動畫時長200
*/
fun setMaxStep(maxStep: Int, duration: Long = 0) {
mMaxStep = maxStep
val progress = (mCurrentStep * 1F / mMaxStep * 100).toInt()
setProgress(progress, duration)
}
/**
* @param currentStep 當(dāng)前走了多少步
* @param duration 默認(rèn)動畫時長200
*/
fun setCurrentStep(currentStep: Int, duration: Long = 200) {
mCurrentStep = currentStep
val progress = (mCurrentStep * 1F / mMaxStep * 100).toInt()
setProgress(progress, duration)
}
/**
* 視圖從窗口分離時
*/
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
valueAnimator?.cancel()
valueAnimator = null
}
private fun dp2px(value: Float): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, value, resources.displayMetrics
)
}
/**
* 計算繪制文字時的基線到中軸線的距離
* @param paint 畫筆
* @return 返回基線的距離
*/
private fun getBaseline(paint: Paint): Float {
val fontMetrics: Paint.FontMetrics = paint.fontMetrics
return (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom
}
}
繪制圓弧是是從3點中開始,它位于0度。比如我們可以試試?yán)L制0到90度的圓弧,多試試幾次,你很快就能明白了額
繪制文本坐標(biāo)
繪制文本橫坐標(biāo)是控件寬度的一半減去字體寬度的一半,繪制文本的縱坐標(biāo)是控件高度的一半加上文字的基線。
文字基線我們看個圖清楚了

Android獲取中線到基線距離
Android獲取中線到基線距離的代碼,實際獲取到的Ascent是負(fù)數(shù)
/**
* 計算繪制文字時的基線到中軸線的距離
* @param paint 畫筆
* @return 返回基線的距離
*/
private fun getBaseline(paint: Paint): Float {
val fontMetrics: Paint.FontMetrics = paint.fontMetrics
return (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom
}
項目地址 :https://github.com/githubwwj/MyAndroid
以上就是Android開發(fā)Kotlin繪制圓弧計步器示例詳解的詳細內(nèi)容,更多關(guān)于Android Kotlin繪制圓弧計步器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringBoot2.0集成MQTT消息推送功能實現(xiàn)
這篇文章主要介紹了SpringBoot2.0集成MQTT消息推送功能實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
java并發(fā)編程之深入理解Synchronized的使用
文詳細講述了線程、進程的關(guān)系及在操作系統(tǒng)中的表現(xiàn),這是多線程學(xué)習(xí)必須了解的基礎(chǔ)。本文將接著講一下Java線程同步中的一個重要的概念synchronized,希望能夠給你有所幫助2021-06-06
JavaWeb入門:HttpResponse和HttpRequest詳解
這篇文章主要介紹了Django的HttpRequest和HttpResponse對象,分享了相關(guān)代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下2021-07-07
使用Mybatis-plus實現(xiàn)時間自動填充(代碼直接可用)
這篇文章主要介紹了使用Mybatis-plus實現(xiàn)時間自動填充(代碼直接可用),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06
Java實現(xiàn)的圖片高質(zhì)量縮放類定義與用法示例
這篇文章主要介紹了Java實現(xiàn)的圖片高質(zhì)量縮放類定義與用法,涉及java針對圖片的運算與轉(zhuǎn)換等相關(guān)操作技巧,需要的朋友可以參考下2017-11-11
SpringMVC中MultipartFile上傳獲取圖片的寬度和高度詳解
本篇文章主要介紹了SpringMVC中MultipartFile上傳獲取圖片的寬度和高度,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05

