Android开发Kotlin实现圆弧计步器示例详解
短信预约 -IT技能 免费直播动态提醒
效果图
定义控件的样式
看完效果后,我们先定义控件的样式
<!-- 自定义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
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
private val mArcRect = RectF()
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)
}
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()
}
fun setMaxStep(maxStep: Int, duration: Long = 0) {
mMaxStep = maxStep
val progress = (mCurrentStep * 1F / mMaxStep * 100).toInt()
setProgress(progress, duration)
}
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
)
}
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是负数
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绘制圆弧计步器的资料请关注编程网其它相关文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341