Android开发Kotlin实现圆弧计步器示例详解

效果图

Android开发Kotlin实现圆弧计步器示例详解_第1张图片

定义控件的样式

看完效果后,我们先定义控件的样式


        

        
        
        
        
        
        
    

自定义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开发Kotlin实现圆弧计步器示例详解_第2张图片

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绘制圆弧计步器的资料请关注脚本之家其它相关文章!

你可能感兴趣的:(Android开发Kotlin实现圆弧计步器示例详解)