RingControlView.kt 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. package com.develop.common.widget
  2. import android.content.Context
  3. import android.graphics.Canvas
  4. import android.graphics.Paint
  5. import android.graphics.Path
  6. import android.util.AttributeSet
  7. import android.util.TypedValue
  8. import android.view.MotionEvent
  9. import android.view.View
  10. import com.develop.base.ext.isBrand011A
  11. import com.develop.base.ext.isBrand062
  12. import com.develop.base.ext.isNightTheme
  13. import kotlin.math.*
  14. class RingControlView @JvmOverloads constructor(
  15. context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
  16. ) : View(context, attrs, defStyleAttr) {
  17. private val fillPath = Path()
  18. private val stubPath = Path()
  19. private val ringPaint = Paint(Paint.ANTI_ALIAS_FLAG) //圆点
  20. private val extraPaint = Paint(Paint.ANTI_ALIAS_FLAG) //圆弧
  21. private val is062 = isBrand062()
  22. private val is011 = isBrand011A()
  23. private val ringStrokeWidth = TypedValue.applyDimension(
  24. TypedValue.COMPLEX_UNIT_DIP, 7f, context.resources.displayMetrics
  25. )
  26. private val controlDotRadius = TypedValue.applyDimension(
  27. TypedValue.COMPLEX_UNIT_DIP, 8f, context.resources.displayMetrics
  28. )
  29. private val ringPadding = (controlDotRadius * 2 - ringStrokeWidth) / 2
  30. private var progress: Float = 0.5f
  31. private var controlling = false
  32. private var controlDotX = 0.0
  33. private var controlDotY = 0.0
  34. var onRingViewListener: OnRingViewListener? = null
  35. init {
  36. ringPaint.color = 0xffE60012.toInt()
  37. ringPaint.style = Paint.Style.STROKE
  38. ringPaint.strokeCap = Paint.Cap.ROUND
  39. ringPaint.strokeWidth = ringStrokeWidth
  40. }
  41. private var minPro = 0f
  42. private var maxPro = 100f
  43. private var isCanTouch = true
  44. override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
  45. super.onSizeChanged(w, h, oldw, oldh)
  46. stubPath.reset()
  47. stubPath.addArc(
  48. ringStrokeWidth / 2 + ringPadding,
  49. ringStrokeWidth / 2 + ringPadding,
  50. w.toFloat() - ringStrokeWidth / 2 - ringPadding,
  51. h.toFloat() - ringStrokeWidth / 2 - ringPadding,
  52. -240f, 300f
  53. )
  54. rebuildFillData(progress)
  55. }
  56. override fun onDraw(canvas: Canvas) {
  57. super.onDraw(canvas)
  58. val nightTheme = isNightTheme()
  59. if (nightTheme) {
  60. extraPaint.color = 0x00000000
  61. } else {
  62. extraPaint.color = 0x60ffffff
  63. }
  64. val radius = width / 2f - ringStrokeWidth - ringPadding
  65. canvas.drawCircle(width / 2f, height / 2f, radius, extraPaint)
  66. ringPaint.color = 0xffffffff.toInt()
  67. canvas.drawPath(stubPath, ringPaint)
  68. if (isNightTheme()) {
  69. ringPaint.color = if (is011) 0xffD51317.toInt() else 0xffE03E52.toInt()
  70. } else {
  71. if (is062){
  72. ringPaint.color = 0xffDAE343.toInt()
  73. }else{
  74. ringPaint.color = 0xffE60012.toInt()
  75. }
  76. }
  77. canvas.drawPath(fillPath, ringPaint)
  78. extraPaint.color = if (nightTheme) if (is011) 0xffD51317.toInt() else 0xffDF3F54.toInt() else if (is062) 0xffDAE343.toInt() else 0xffFFA627.toInt()
  79. // -150 ~ 150
  80. val angle = 300.0 * progress - 150.0
  81. val dotRadius = width / 2 - ringPadding - ringStrokeWidth / 2
  82. val dotX = sin(Math.toRadians(angle)) * dotRadius + width / 2
  83. val dotY = height / 2 - cos(Math.toRadians(angle)) * dotRadius
  84. canvas.drawCircle(
  85. dotX.toFloat(), dotY.toFloat(), controlDotRadius, extraPaint
  86. )
  87. }
  88. private fun rebuildFillData(percent: Float) {
  89. val w = width
  90. val h = height
  91. fillPath.reset()
  92. fillPath.addArc(
  93. ringStrokeWidth / 2 + ringPadding,
  94. ringStrokeWidth / 2 + ringPadding,
  95. w.toFloat() - ringStrokeWidth / 2 - ringPadding,
  96. h.toFloat() - ringStrokeWidth / 2 - ringPadding,
  97. -240f, percent * 300f
  98. )
  99. // -150 ~ 150
  100. val angle = 300.0 * percent - 150.0
  101. val dotRadius = width / 2 - ringPadding - ringStrokeWidth / 2
  102. controlDotX = sin(Math.toRadians(angle)) * dotRadius + width / 2
  103. controlDotY = height / 2 - cos(Math.toRadians(angle)) * dotRadius
  104. }
  105. override fun onTouchEvent(event: MotionEvent): Boolean {
  106. if (!isCanTouch) return false
  107. val moveX = event.x
  108. val moveY = event.y
  109. val distance = sqrt(
  110. (controlDotX - moveX) * (controlDotX - moveX) + (controlDotY - moveY) * (controlDotY - moveY)
  111. )
  112. when (event.action) {
  113. MotionEvent.ACTION_DOWN -> {
  114. if (distance > 60) {
  115. return super.onTouchEvent(event)
  116. }
  117. controlling = true
  118. calculateCurrentPercent(moveX, moveY)
  119. return true
  120. }
  121. MotionEvent.ACTION_MOVE -> {
  122. if (controlling) {
  123. calculateCurrentPercent(moveX, moveY)
  124. return true
  125. }
  126. }
  127. MotionEvent.ACTION_UP -> {
  128. if (controlling) {
  129. controlling = false
  130. calculateCurrentPercent(moveX, moveY)
  131. return true
  132. }
  133. }
  134. }
  135. return super.onTouchEvent(event)
  136. }
  137. private fun calculateCurrentPercent(moveX: Float, moveY: Float) {
  138. val distance = sqrt(
  139. (width / 2.0 - moveX) * (width / 2.0 - moveX) + (height / 2.0 - moveY) * (height / 2.0 - moveY)
  140. )
  141. val px = width / 2f - moveX
  142. val degree = acos(px / distance)
  143. var angle = Math.toDegrees(degree)
  144. if (moveY > height / 2) {
  145. angle = if (moveX < width / 2) {
  146. -angle
  147. } else {
  148. 360 - angle
  149. }
  150. }
  151. val percent = (angle + 60f) / 300f
  152. if (abs(percent - this.progress) > 0.2f) {
  153. return
  154. }
  155. setProgress(percent.toFloat())
  156. onRingViewListener?.onProgressChange((progress * (maxPro - minPro) + minPro).toInt())
  157. }
  158. private fun setProgress(progress: Float) {
  159. if (progress > 1) {
  160. this.progress = 1f
  161. } else if (progress < 0) {
  162. this.progress = 0f
  163. } else {
  164. this.progress = progress
  165. }
  166. if (width > 0) {
  167. rebuildFillData(this.progress)
  168. }
  169. postInvalidate()
  170. }
  171. fun updateProgress(value: Int) {
  172. progress = (value - minPro) / (maxPro - minPro)
  173. setProgress(progress)
  174. }
  175. /**
  176. * 设置范围默认0-100
  177. *
  178. */
  179. fun setRange(min: Float, max: Float) {
  180. minPro = min
  181. maxPro = max
  182. }
  183. fun setCanTouch(touch: Boolean) {
  184. isCanTouch = touch
  185. }
  186. interface OnRingViewListener {
  187. fun onProgressChange(progress: Int)
  188. }
  189. }