分段進(jìn)度條&分段圖標(biāo)抖動動畫的實現(xiàn)
先上圖银觅,https://github.com/Warkey1991/Jackview覺得有用的話榆俺,希望給個star
?該自定義View有三個問題需要一一實現(xiàn)
1. **如何實現(xiàn)分段** 趴梢,
2. **如何用canvas 實現(xiàn)抖動的動畫效果**
3. **如何識別點(diǎn)擊區(qū)域的點(diǎn)擊事件**
?以下就針對三個問題提供解決思路和實戰(zhàn)代碼
a.本例中采用的是均分三段,定義一個數(shù)組A = {3,5,8},再將每一段根據(jù)A 中的A[i]的值均分藐鹤,
如圖
繪制進(jìn)度的時候,需要先找出當(dāng)前進(jìn)度progress 所在的區(qū)間赂韵,然后分段繪制娱节。具體代碼如下:
```kotlin
//查找所在區(qū)間的索引
private fun findRange(): Int {
? ? if (progress >= segments.last().progress) {
? ? ? ? progressPadding = 0
? ? ? ? return segments.size - 1
? ? }
? ? var index = 0
? ? for (i in 0 until segments.size - 1) {
? ? ? ? if (progress in segments[i].progress..segments[i + 1].progress) {
? ? ? ? ? ? index = i + 1
? ? ? ? }
? ? }
? ? if (index >= 1) {
? ? ? ? currentSegmentProgress = progress - segments[index - 1].progress
? ? }
? ? return index
}
//繪制
? override fun onDraw(canvas: Canvas?) {
? ? ? ? super.onDraw(canvas)
? ? ? ? rect.set(0f, (height / 2 - dip2px(5f)).toFloat(), width.toFloat(), (height / 2 + dip2px(5f)).toFloat())
? ? ? ? canvas?.drawRoundRect(rect, horizontalRound, horizontalRound, bgPaint)
? ? ? ? val index = findRange()
? ? ? ? var eachWidth = (width - lightBitmap.width) / segments.size
? ? ? ? if (index == segments.size - 1) {
? ? ? ? ? ? eachWidth = width / segments.size
? ? ? ? }
? ? ? ? //轉(zhuǎn)為float,可以預(yù)防divide by zero 的異常
? ? ? ? val smallEachWidth = eachWidth.toFloat() / segmentMaxs[index].toFloat()
? ? ? ? var x = eachWidth * index + smallEachWidth * currentSegmentProgress
? ? ? ? x = x.coerceAtMost(width.toFloat())
? ? ? ? rect.right = x
? ? ? ? canvas?.drawRoundRect(rect, horizontalRound, horizontalRound, progressPaint)
? ? ? ? drawBitmaps(canvas)
? ? }
```
b.用canvas 實現(xiàn)抖動的動畫效果:
? 分析了單獨(dú)的View用rotate 動畫實現(xiàn)的方式,可以得出結(jié)論祭示,只需要每次繪制的時候旋轉(zhuǎn)canvas畫布即可肄满,每次旋轉(zhuǎn)的弧度需要自己自行定義,本例中使用的是`listOf(-5.0f, -4f, -3f, -2f, -1f, 2f, 3f, 4f, 5f,
? ? ? ? ? ? -5.0f, -4f, -3f, -2f, -1f, 2f, 3f, 4f, 5f,
? ? ? ? ? ? -5.0f, -4f, -3f, -2f, -1f, 2f, 3f, 4f, 5f,
? ? ? ? ? ? 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f,
? ? ? ? ? ? 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f,
? ? ? ? ? ? 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f)`
抖動三次停頓一會的效果,定義一個數(shù)組的索引index = 0, 從0 開始稠歉,開啟一個線程掰担,每16ms,index+1,重新繪制即可實現(xiàn),
代碼如下:
```kotlin
private fun rotate() {
? ? ? ? thread {
? ? ? ? ? ? while (!needStopThread) {
? ? ? ? ? ? ? ? if (animCount == rotateAngle.size) {
? ? ? ? ? ? ? ? ? ? animCount = 0
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? Thread.sleep(16)
? ? ? ? ? ? ? ? post {
? ? ? ? ? ? ? ? ? ? invalidate()
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? animCount++
? ? ? ? ? ? }
? ? ? ? }
? ? }
```
繪制核心代碼:
```kotlin
//獲取旋轉(zhuǎn)的弧度
val degrees = rotateAngle[animCount % rotateAngle.size]
canvas?.save()
//以中心點(diǎn)旋轉(zhuǎn)
canvas?.rotate(degrees, rectF.centerX(), rectF.centerY())
canvas?.drawBitmap(imageBitmap, null, innerRectF, null)
canvas?.restore()
```
c. 添加點(diǎn)擊區(qū)域的點(diǎn)擊事件:
三個紅色節(jié)點(diǎn)的坐標(biāo)位置在繪制的時候需要保存起來`? ? private var touchCallBackRectF = mutableListOf<RectF>()`,定義一個變量用于保存節(jié)點(diǎn)的區(qū)域位置信息怒炸。
復(fù)寫onTouchEvent 方法带饱,在MotionEvent.ACTION_UP 中判斷該點(diǎn)擊的位置是否在touchCallBackRectF 中,找出對應(yīng)的位置阅羹。
```kotlin
@SuppressLint("ClickableViewAccessibility")
? ? override fun onTouchEvent(event: MotionEvent?): Boolean {
? ? ? ? when (event?.action) {
? ? ? ? ? ? MotionEvent.ACTION_UP -> {
? ? ? ? ? ? ? ? val x = event.x
? ? ? ? ? ? ? ? val y = event.y
? ? ? ? ? ? ? ? for (i in 0 until touchCallBackRectF.size) {
? ? ? ? ? ? ? ? ? ? if (touchCallBackRectF[i].contains(x, y)) {
? ? ? ? ? ? ? ? ? ? ? ? segments[i].status = BoxStatus.AVAILABLE.ordinal
? ? ? ? ? ? ? ? ? ? ? ? Toast.makeText(context, "click index:${i}", Toast.LENGTH_SHORT).show()
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return true
? ? }
```