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

 更新时间:2022年06月27日 09:04:22   作者:as_pixar  
这篇文章主要为大家介绍了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绘制圆弧计步器的资料请关注脚本之家其它相关文章!

相关文章

  • java实习--每天打卡十道面试题!

    java实习--每天打卡十道面试题!

    临近秋招,备战暑期实习,祝大家每天进步亿点点!本篇文章准备了十道java的常用面试题,希望能够给大家提供帮助,最后祝大家面试成功,进入自己心仪的大厂
    2021-06-06
  • Java BigDecimal案例详解

    Java BigDecimal案例详解

    这篇文章主要介绍了Java BigDecimal案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • Spring Boot 2.7.6整合redis与低版本的区别

    Spring Boot 2.7.6整合redis与低版本的区别

    这篇文章主要介绍了Spring Boot 2.7.6整合redis与低版本的区别,文中补充介绍了SpringBoot各个版本使用Redis之间的区别实例讲解,需要的朋友可以参考下
    2023-02-02
  • IntelliJ IDEA 中使用jRebel进行 Java 热部署教程图解

    IntelliJ IDEA 中使用jRebel进行 Java 热部署教程图解

    Rebel是一款JAVA虚拟机插件,它使得JAVA程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。本文通过图文并茂的形式给大家介绍了IntelliJ IDEA 中使用jRebel进行 Java 热部署教程图解,需要的朋友参考下吧
    2018-04-04
  • 详解JVM 中的StringTable

    详解JVM 中的StringTable

    这篇文章主要介绍了JVM 中的StringTable,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-03-03
  • SpringBoot之RabbitMQ的使用方法

    SpringBoot之RabbitMQ的使用方法

    这篇文章主要介绍了SpringBoot之RabbitMQ的使用方法,详细的介绍了2种模式,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • 使用dubbo+zookeeper+spring boot构建服务的方法详解

    使用dubbo+zookeeper+spring boot构建服务的方法详解

    这篇文章主要给大家介绍了关于如何使用dubbo+zookeeper+spring boot构建服务的相关资料,文中通过示例代码及图片介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧
    2018-05-05
  • 基于Java实现获取本地IP地址和主机名

    基于Java实现获取本地IP地址和主机名

    这篇文章主要介绍了基于Java实现获取本地IP地址和主机名,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • Java实现HttpGet请求传body参数

    Java实现HttpGet请求传body参数

    这篇文章主要为大家详细介绍了Java实现HttpGet请求传body参数的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2024-02-02
  • Java通俗易懂讲解泛型

    Java通俗易懂讲解泛型

    在正式进入内容之前说明一下:泛型的内容太多,也太复杂。这里因为Java中写数据结构的时候会使用到,所以加上。关于泛型我找了挺多文章,再结合自己的理解,尽可能将其讲清楚。不求会使用泛型,只要求后面数据结构出现泛型的时候能够知道是在干什么即可
    2022-05-05

最新评论