Android開發Kotlin實現圓弧計步器示例詳解
效果圖
定義控件的樣式
看完效果後,我們先定義控件的樣式
<!-- 自定義View的名字 StepView --> <!-- name 屬性名稱 format 格式 string 文字 color 顏色 dimension 字體大小 integer 數字 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 { /** * 當前走瞭多少步 */ private var mCurrentStep: Int = 0 /** * 最大走多少步,比如兩萬步 20000步 * 默認100步,有個成語50步笑100步 */ private var mMaxStep: Int = 100 /** * 單位:步 %等 */ private var mUnit: String? /** * 設置圓弧的邊框線寬度 */ private var mBorderWidth: Float = dp2px(6F) /** * 設置外部圓弧的顏色 */ private var mOuterColor: Int = resources.getColor(android.R.color.holo_blue_light) /** * 設置內部圓弧的顏色 */ private var mInnerColor: Int = resources.getColor(android.R.color.holo_red_light) /** * 圓弧畫筆 */ private var mArcPaint: Paint = Paint() /** * 文本畫筆,用於繪畫走瞭多少步 */ private var mTextPaint: Paint = Paint() /** * 日志過濾標簽 */ private val TAG = javaClass.simpleName /** * 圓弧起始角度 */ private var mStartAngle = 135F /** * 圓弧從起始角度開始,掃描過的角度 */ private var mSweepAngle = mStartAngle * 2 /** * 比如用於獲取一個圓弧的矩形,onDraw()方法會調用多次,不必每次都創建Rect()對象 */ private val mArcRect = RectF() /** * 比如用於獲取文字的大小,onDraw()方法會調用多次,不必每次都創建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() } /** * 設置 圓弧畫筆用於繪制圓弧 和 文本畫筆,用於繪畫走瞭多少步 */ private fun setPaint() { // 畫筆的顏色 mArcPaint.color = mOuterColor // 抗抖動 mArcPaint.isDither = true // 抗鋸齒 mArcPaint.isAntiAlias = true // 畫筆的樣式描邊,筆劃突出為半圓 mArcPaint.style = Paint.Style.STROKE // 設置描邊的線帽樣式 mArcPaint.strokeCap = Paint.Cap.ROUND // 設置描邊的寬度 mArcPaint.strokeWidth = mBorderWidth // 畫筆的顏色 mTextPaint.color = currentTextColor // 抗抖動 mTextPaint.isDither = true // 抗鋸齒 mTextPaint.isAntiAlias = true // 畫筆的樣式描邊,筆劃突出為半圓 mTextPaint.style = Paint.Style.FILL // 設置描邊的線帽樣式 mTextPaint.strokeCap = Paint.Cap.ROUND // 設置文本大小 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 } /** * 設置控件的寬高 */ setMeasuredDimension(result, result) } override fun onDraw(canvas: Canvas?) { // 將矩形設置為 (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) // 繪制內部部圓弧 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) // 繪制文本,第二個參數文本的起始索引,第三個參數要繪制的文字長度 // 開始繪制文字的x 坐標 y 坐標 canvas?.drawText(stepText, 0, stepText.length, textX, textY, mTextPaint) } /** * @param progress 進入0-100 之間 * @param duration 動畫時長,默認 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 默認動畫時長200 */ fun setMaxStep(maxStep: Int, duration: Long = 0) { mMaxStep = maxStep val progress = (mCurrentStep * 1F / mMaxStep * 100).toInt() setProgress(progress, duration) } /** * @param currentStep 當前走瞭多少步 * @param duration 默認動畫時長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度。比如我們可以試試繪制0到90度的圓弧,多試試幾次,你很快就能明白瞭額
繪制文本坐標
繪制文本橫坐標是控件寬度的一半減去字體寬度的一半,繪制文本的縱坐標是控件高度的一半加上文字的基線。
文字基線我們看個圖清楚瞭
Android獲取中線到基線距離
Android獲取中線到基線距離的代碼,實際獲取到的Ascent是負數
/** * 計算繪制文字時的基線到中軸線的距離 * @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開發Kotlin繪制圓弧計步器示例詳解的詳細內容,更多關於Android Kotlin繪制圓弧計步器的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- Android新建水平節點進度條示例
- android實現倒計時動態圈
- android實現圓環倒計時控件
- 詳解Android如何自定義view實現圓形進度條
- Android自定義view實現圓形進度條效果