一、概述
当下答题类APP很多,都需要用到带动画的进度条。话不多说,实现效果直接上图:
实现下图效果:
二、开始编码
代码中动态改变横向进度条各种状态很简单(比如进度,圆角,颜色,开始方向),直接代码中动态改变progressBar的progressDrawable或者background就行。
val gradientDrawable = GradientDrawable()
//cornerRadii是长度为8的float数组,每两个值对应分别为左上,右上,右下,左下,两个值分别为x和y方向的半径
gradientDrawable.cornerRadii = getCornerArray(cornerType)
gradientDrawable.setColor(color)
progressDrawable = ClipDrawable(gradientDrawable, gravity, ClipDrawable.HORIZONTAL)
val backgroundDrawable = GradientDrawable()
backgroundDrawable.cornerRadii = getCornerArray(cornerType)
backgroundDrawable.setColor(backgroundColor)
background = backgroundDrawable
有一点值得说明的是,如果drawable所有属性都相同的话,重复设置会导致progressBar空白问题,这时可以根据id,颜色,圆角等属性判断是否重新设置。
接着就是动画,我们使用属性动画ValueAnimator来控制进度的变化。
fun startAnim(progress: Int, onAnimationEnd: () -> Unit = {}) {
animator = ValueAnimator.ofFloat(0f, progress.toFloat())
animator?.duration = duration
animator?.addUpdateListener { animation ->
val value = animation?.animatedValue as Float
setProgress(value.toInt())
}
animator?.addListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {
}
override fun onAnimationEnd(animation: Animator?) {
onAnimationEnd()
}
override fun onAnimationCancel(animation: Animator?) {
}
override fun onAnimationStart(animation: Animator?) {
}
})
animator?.start()
}
进度条占整横屏的很好做,直接写就行,但是两种答案PK的稍稍有些问题需要注意。如果没圆角也同样很简单,但是如果两边要求圆角,那么问题来了。比如如果有一方的进度很小,小到不足以画完圆角,那么两个进度条就对不齐或者进度小的一方几乎画成直角。如下两种:
这里就做简单的进度判断,如果小于某个数值就固定为某数值。如果有其他办法可以使用其他解决方案,方法不唯一,解决问题才是关键。
if (yesPer < 3 && yesPer > 0) {//防止进度太小导致圆角问题
yesPerInt = 3
noPerInt = 97
} else if (noPer < 3 && noPer > 0) {
yesPerInt = 97
noPerInt = 3
}
至于两条PK的宽度,有两种解决方案,其一即整体重叠,背景使用透明色,进度合并成100就行。其二,根据进度值设置layout_weight和maxProgress。
二、写在最后
刚开始没写圆角,直接drawable画Rect,还觉得挺简单,结果发现有圆角,OMG,画一端圆角和判断有点复杂,接着自定义view好像有点困难啊,那就从头开始,重新写吧。一顿猛虎操作,自觉还是很简单啊,结果progressBar放在列表中之后才发现一系列令人不解的bug,不在bug中成长就在bug中发疯。希望后来者可以不用像我一样发疯。
源码下载