经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
Android开发Kotlin实现圆弧计步器示例详解
来源:jb51  时间:2022/6/27 10:53:32  对本文有异议

效果图

定义控件的样式

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

  1. <!-- 自定义View的名字 StepView -->
  2. <!-- name 属性名称 format 格式
  3. string 文字 color 颜色 dimension 字体大小
  4. integer 数字 reference 资源或者颜色
  5. -->
  6. <declare-styleable name="StepView">
  7. <attr name="borderWidth" format="dimension" />
  8. <attr name="outColor" format="color" />
  9. <attr name="innerColor" format="color" />
  10. <attr name="unit" format="string" />
  11. <attr name="currentStep" format="integer" />
  12. <attr name="maxStep" format="integer" />
  13. </declare-styleable>

自定义StepView

接下来我们自定义一个StepView(记步的View)

  1. package cn.wwj.customview.widget
  2. import android.animation.ValueAnimator
  3. import android.content.Context
  4. import android.graphics.Canvas
  5. import android.graphics.Paint
  6. import android.graphics.Rect
  7. import android.graphics.RectF
  8. import android.util.AttributeSet
  9. import android.util.Log
  10. import android.util.TypedValue
  11. import android.view.animation.AccelerateInterpolator
  12. import androidx.appcompat.widget.AppCompatTextView
  13. import cn.wwj.customview.R
  14. class StepView : AppCompatTextView {
  15. /**
  16. * 当前走了多少步
  17. */
  18. private var mCurrentStep: Int = 0
  19. /**
  20. * 最大走多少步,比如两万步 20000步
  21. * 默认100步,有个成语50步笑100步
  22. */
  23. private var mMaxStep: Int = 100
  24. /**
  25. * 单位:步 %等
  26. */
  27. private var mUnit: String?
  28. /**
  29. * 设置圆弧的边框线宽度
  30. */
  31. private var mBorderWidth: Float = dp2px(6F)
  32. /**
  33. * 设置外部圆弧的颜色
  34. */
  35. private var mOuterColor: Int = resources.getColor(android.R.color.holo_blue_light)
  36. /**
  37. * 设置内部圆弧的颜色
  38. */
  39. private var mInnerColor: Int = resources.getColor(android.R.color.holo_red_light)
  40. /**
  41. * 圆弧画笔
  42. */
  43. private var mArcPaint: Paint = Paint()
  44. /**
  45. * 文本画笔,用于绘画走了多少步
  46. */
  47. private var mTextPaint: Paint = Paint()
  48. /**
  49. * 日志过滤标签
  50. */
  51. private val TAG = javaClass.simpleName
  52. /**
  53. * 圆弧起始角度
  54. */
  55. private var mStartAngle = 135F
  56. /**
  57. * 圆弧从起始角度开始,扫描过的角度
  58. */
  59. private var mSweepAngle = mStartAngle * 2
  60. /**
  61. * 比如用于获取一个圆弧的矩形,onDraw()方法会调用多次,不必每次都创建Rect()对象
  62. */
  63. private val mArcRect = RectF()
  64. /**
  65. * 比如用于获取文字的大小,onDraw()方法会调用多次,不必每次都创建Rect()对象
  66. * 用同一个对象即可
  67. */
  68. private val mTextRect = Rect()
  69. /**
  70. * 值动画师
  71. */
  72. private var valueAnimator: ValueAnimator? = null
  73. /**
  74. * 最大进度
  75. */
  76. private val MAX_PROGRESS = 100F
  77. constructor(context: Context)
  78. : this(context, null)
  79. constructor(context: Context, attrs: AttributeSet?)
  80. : this(context, attrs, 0)
  81. constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int)
  82. : super(context, attrs, defStyleAttr) {
  83. val appearance = context.obtainStyledAttributes(attrs, R.styleable.StepView)
  84. mBorderWidth = appearance.getDimension(R.styleable.StepView_borderWidth, mBorderWidth)
  85. mOuterColor = appearance.getColor(R.styleable.StepView_outColor, mOuterColor)
  86. mInnerColor = appearance.getColor(R.styleable.StepView_innerColor, mInnerColor)
  87. mUnit = appearance.getString(R.styleable.StepView_unit)
  88. mCurrentStep = appearance.getInt(R.styleable.StepView_currentStep, 0)
  89. mMaxStep = appearance.getInt(R.styleable.StepView_maxStep, mMaxStep)
  90. appearance.recycle()
  91. setPaint()
  92. }
  93. /**
  94. * 设置 圆弧画笔用于绘制圆弧 和 文本画笔,用于绘画走了多少步
  95. */
  96. private fun setPaint() {
  97. // 画笔的颜色
  98. mArcPaint.color = mOuterColor
  99. // 抗抖动
  100. mArcPaint.isDither = true
  101. // 抗锯齿
  102. mArcPaint.isAntiAlias = true
  103. // 画笔的样式描边,笔划突出为半圆
  104. mArcPaint.style = Paint.Style.STROKE
  105. // 设置描边的线帽样式
  106. mArcPaint.strokeCap = Paint.Cap.ROUND
  107. // 设置描边的宽度
  108. mArcPaint.strokeWidth = mBorderWidth
  109. // 画笔的颜色
  110. mTextPaint.color = currentTextColor
  111. // 抗抖动
  112. mTextPaint.isDither = true
  113. // 抗锯齿
  114. mTextPaint.isAntiAlias = true
  115. // 画笔的样式描边,笔划突出为半圆
  116. mTextPaint.style = Paint.Style.FILL
  117. // 设置描边的线帽样式
  118. mTextPaint.strokeCap = Paint.Cap.ROUND
  119. // 设置文本大小
  120. mTextPaint.textSize = textSize
  121. }
  122. override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
  123. super.onMeasure(widthMeasureSpec, heightMeasureSpec)
  124. /**
  125. * 获取控件的宽高
  126. */
  127. val widthSize = MeasureSpec.getSize(widthMeasureSpec)
  128. val heightSize = MeasureSpec.getSize(heightMeasureSpec)
  129. /**
  130. * 如果宽度大于高度,取高度
  131. * 否则取宽度
  132. */
  133. val result = if (widthSize > heightSize) {
  134. heightSize
  135. } else {
  136. widthSize
  137. }
  138. /**
  139. * 设置控件的宽高
  140. */
  141. setMeasuredDimension(result, result)
  142. }
  143. override fun onDraw(canvas: Canvas?) {
  144. // 将矩形设置为 (0,0,0,0)
  145. mArcRect.setEmpty()
  146. mTextRect.setEmpty()
  147. // 圆弧矩形左边距,顶边距,右边距,底边距
  148. val left = mBorderWidth / 2
  149. val top = mBorderWidth / 2
  150. val right = width - mBorderWidth / 2
  151. val bottom = height - mBorderWidth / 2
  152. mArcRect.set(left, top, right, bottom)
  153. // 绘制外部圆弧
  154. mArcPaint.color = mOuterColor
  155. canvas?.drawArc(mArcRect, mStartAngle, mSweepAngle, false, mArcPaint)
  156. // 绘制内部部圆弧
  157. mArcPaint.color = mInnerColor
  158. val sweepAngle = mCurrentStep * 1F / mMaxStep * mSweepAngle
  159. canvas?.drawArc(mArcRect, mStartAngle, sweepAngle, false, mArcPaint)
  160. val stepText = if (mUnit != null) {
  161. "$mCurrentStep $mUnit"
  162. } else {
  163. mCurrentStep.toString()
  164. }
  165. // 获取文本的宽高
  166. mTextPaint.getTextBounds(stepText, 0, stepText.length, mTextRect)
  167. val textX = width / 2F - mTextRect.width() / 2
  168. val textY = height / 2F + getBaseline(mTextPaint)
  169. // 绘制文本,第二个参数文本的起始索引,第三个参数要绘制的文字长度
  170. // 开始绘制文字的x 坐标 y 坐标
  171. canvas?.drawText(stepText, 0, stepText.length, textX, textY, mTextPaint)
  172. }
  173. /**
  174. * @param progress 进入0-100 之间
  175. * @param duration 动画时长,默认 350毫秒
  176. */
  177. fun setProgress(progress: Int, duration: Long = 350) {
  178. valueAnimator?.cancel()
  179. valueAnimator = null
  180. val step = (progress / MAX_PROGRESS * mMaxStep).toInt()
  181. valueAnimator = ValueAnimator.ofInt(mCurrentStep, step.coerceAtMost(mMaxStep))
  182. valueAnimator?.duration = duration
  183. valueAnimator?.interpolator = AccelerateInterpolator()
  184. valueAnimator?.addUpdateListener {
  185. mCurrentStep = it.animatedValue as Int
  186. Log.d(TAG, "------$mCurrentStep")
  187. invalidate()
  188. }
  189. valueAnimator?.startDelay = 10
  190. valueAnimator?.start()
  191. }
  192. /**
  193. * @param maxStep 最多走多少步,比如2000步
  194. * @param duration 默认动画时长200
  195. */
  196. fun setMaxStep(maxStep: Int, duration: Long = 0) {
  197. mMaxStep = maxStep
  198. val progress = (mCurrentStep * 1F / mMaxStep * 100).toInt()
  199. setProgress(progress, duration)
  200. }
  201. /**
  202. * @param currentStep 当前走了多少步
  203. * @param duration 默认动画时长200
  204. */
  205. fun setCurrentStep(currentStep: Int, duration: Long = 200) {
  206. mCurrentStep = currentStep
  207. val progress = (mCurrentStep * 1F / mMaxStep * 100).toInt()
  208. setProgress(progress, duration)
  209. }
  210. /**
  211. * 视图从窗口分离时
  212. */
  213. override fun onDetachedFromWindow() {
  214. super.onDetachedFromWindow()
  215. valueAnimator?.cancel()
  216. valueAnimator = null
  217. }
  218. private fun dp2px(value: Float): Float {
  219. return TypedValue.applyDimension(
  220. TypedValue.COMPLEX_UNIT_DIP, value, resources.displayMetrics
  221. )
  222. }
  223. /**
  224. * 计算绘制文字时的基线到中轴线的距离
  225. * @param paint 画笔
  226. * @return 返回基线的距离
  227. */
  228. private fun getBaseline(paint: Paint): Float {
  229. val fontMetrics: Paint.FontMetrics = paint.fontMetrics
  230. return (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom
  231. }
  232. }

绘制圆弧是是从3点中开始,它位于0度。比如我们可以试试绘制0到90度的圆弧,多试试几次,你很快就能明白了额

绘制文本坐标

绘制文本横坐标是控件宽度的一半减去字体宽度的一半,绘制文本的纵坐标是控件高度的一半加上文字的基线。

文字基线我们看个图清楚了

Android获取中线到基线距离

Android获取中线到基线距离的代码,实际获取到的Ascent是负数

  1. /**
  2. * 计算绘制文字时的基线到中轴线的距离
  3. * @param paint 画笔
  4. * @return 返回基线的距离
  5. */
  6. private fun getBaseline(paint: Paint): Float {
  7. val fontMetrics: Paint.FontMetrics = paint.fontMetrics
  8. return (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom
  9. }

项目地址 :https://github.com/githubwwj/MyAndroid

以上就是Android开发Kotlin绘制圆弧计步器示例详解的详细内容,更多关于Android Kotlin绘制圆弧计步器的资料请关注w3xue其它相关文章!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号