效果图

定义控件的样式
看完效果后,我们先定义控件的样式
- <!-- 自定义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绘制圆弧计步器的资料请关注w3xue其它相关文章!