分段进度条&分段图标抖动动画的实现
先上图,https://github.com/Warkey1991/Jackview觉得有用的话,希望给个star
该自定义View有三个问题需要一一实现
1. **如何实现分段** ,
2. **如何用canvas 实现抖动的动画效果**
3. **如何识别点击区域的点击事件**
以下就针对三个问题提供解决思路和实战代码
a.本例中采用的是均分三段,定义一个数组A = {3,5,8},再将每一段根据A 中的A[i]的值均分,
如图
绘制进度的时候,需要先找出当前进度progress 所在的区间,然后分段绘制。具体代码如下:
```kotlin
//查找所在区间的索引
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
}
//转为float,可以预防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 实现抖动的动画效果:
分析了单独的View用rotate 动画实现的方式,可以得出结论,只需要每次绘制的时候旋转canvas画布即可,每次旋转的弧度需要自己自行定义,本例中使用的是`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)`
抖动三次停顿一会的效果,定义一个数组的索引index = 0, 从0 开始,开启一个线程,每16ms,index+1,重新绘制即可实现,
代码如下:
```kotlin
private fun rotate() {
thread {
while (!needStopThread) {
if (animCount == rotateAngle.size) {
animCount = 0
}
Thread.sleep(16)
post {
invalidate()
}
animCount++
}
}
}
```
绘制核心代码:
```kotlin
//获取旋转的弧度
val degrees = rotateAngle[animCount % rotateAngle.size]
canvas?.save()
//以中心点旋转
canvas?.rotate(degrees, rectF.centerX(), rectF.centerY())
canvas?.drawBitmap(imageBitmap, null, innerRectF, null)
canvas?.restore()
```
c. 添加点击区域的点击事件:
三个红色节点的坐标位置在绘制的时候需要保存起来` private var touchCallBackRectF = mutableListOf
复写onTouchEvent 方法,在MotionEvent.ACTION_UP 中判断该点击的位置是否在touchCallBackRectF 中,找出对应的位置。
```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
}
```