LoadingButtonAndroid源码剖析:理解CircularProgressAnimatedDrawable实现机制
【免费下载链接】LoadingButtonAndroidA button to substitute the ProgressDialog项目地址: https://gitcode.com/gh_mirrors/lo/LoadingButtonAndroid
LoadingButtonAndroid是一个功能强大的Android按钮组件,专为替代传统ProgressDialog而设计。它通过在按钮内部集成动画进度指示器,提供了更流畅的用户体验和更现代的视觉效果。本文将深入剖析其核心动画组件CircularProgressAnimatedDrawable的实现机制,帮助开发者理解其工作原理和使用方法。
CircularProgressAnimatedDrawable的核心功能与定位
CircularProgressAnimatedDrawable是LoadingButtonAndroid库中的关键动画组件,负责实现按钮内部的圆形进度动画效果。该类位于项目的loading-button-android/src/main/java/io/writeopia/loadingbutton/animatedDrawables/目录下,是实现按钮加载状态视觉反馈的核心。
作为一个实现了Animatable接口的Drawable,CircularProgressAnimatedDrawable具备以下核心能力:
- 支持两种进度模式:确定进度(DETERMINATE)和不确定进度(INDETERMINATE)
- 提供平滑的圆形进度动画效果
- 可自定义进度条颜色、宽度等视觉属性
- 与按钮组件紧密集成,提供状态同步和重绘机制
类结构与核心属性解析
CircularProgressAnimatedDrawable的类定义如下:
internal class CircularProgressAnimatedDrawable( private val progressButton: ProgressButton, private val borderWidth: Float, arcColor: Int, var progressType: ProgressType = ProgressType.INDETERMINATE ) : Drawable(), Animatable这个类包含几个关键属性:
- progressButton: 对ProgressButton接口的引用,用于与按钮组件通信和触发重绘
- borderWidth: 进度条的宽度
- arcColor: 进度条的颜色
- progressType: 进度类型,通过
ProgressType枚举定义,包含DETERMINATE和INDETERMINATE两个选项
其中,ProgressType枚举定义在ProgressType.kt文件中:
enum class ProgressType { DETERMINATE, INDETERMINATE }绘制机制:如何实现圆形进度条
CircularProgressAnimatedDrawable的绘制逻辑主要通过重写Drawable的draw方法实现:
override fun draw(canvas: Canvas) { val (startAngle, sweepAngle) = getAngles() canvas.drawArc(fBounds, startAngle, sweepAngle, false, paint) }这里的关键是getAngles()方法,它根据当前的进度类型和动画状态计算出圆弧的起始角度和扫过角度:
private fun getAngles(): Pair<Float, Float> = when (progressType) { ProgressType.DETERMINATE -> { -90F to progress * 3.6F } ProgressType.INDETERMINATE -> { if (modeAppearing) { (currentGlobalAngle - currentGlobalAngleOffset) to currentSweepAngle + MIN_SWEEP_ANGLE } else { (currentGlobalAngle - currentGlobalAngleOffset + currentSweepAngle) to 360F - currentSweepAngle - MIN_SWEEP_ANGLE } } }对于确定进度模式(DETERMINATE),计算方式非常直接:起始角度固定为-90度(12点方向),扫过角度则根据progress值计算(progress * 3.6,因为100%对应360度)。
对于不确定进度模式(INDETERMINATE),则通过currentGlobalAngle、currentGlobalAngleOffset和currentSweepAngle等变量计算动态变化的角度,实现旋转动画效果。
动画实现:双动画协同工作
CircularProgressAnimatedDrawable的动画效果通过两个ValueAnimator协同工作实现:
private val indeterminateAnimator = AnimatorSet().apply { playTogether( angleValueAnimator(LinearInterpolator()), sweepValueAnimator(AccelerateDecelerateInterpolator()) ) }角度动画(angleValueAnimator)
角度动画负责控制进度条的旋转效果:
private fun angleValueAnimator(timeInterpolator: TimeInterpolator): ValueAnimator = ValueAnimator.ofFloat(0F, 360F).apply { interpolator = timeInterpolator duration = ANGLE_ANIMATOR_DURATION // 2000L repeatCount = ValueAnimator.INFINITE addUpdateListener { animation -> currentGlobalAngle = animation.animatedValue as Float } }这个动画从0度到360度循环播放,周期为2000毫秒,使用线性插值器,确保匀速旋转。
扫过角度动画(sweepValueAnimator)
扫过角度动画负责控制进度条的长度变化:
private fun sweepValueAnimator(timeInterpolator: TimeInterpolator): ValueAnimator = ValueAnimator.ofFloat(0F, 360F - 2 * MIN_SWEEP_ANGLE).apply { interpolator = timeInterpolator duration = SWEEP_ANIMATOR_DURATION // 700L repeatCount = ValueAnimator.INFINITE addUpdateListener { animation -> currentSweepAngle = animation.animatedValue as Float // 触发重绘逻辑 } addListener(object : AnimatorListenerAdapter() { override fun onAnimationRepeat(animation: Animator) { toggleSweep() // 切换进度条增长/缩短模式 } }) }这个动画周期为700毫秒,使用加速减速插值器,使进度条的长度变化更加自然。在动画重复时,会调用toggleSweep()方法切换进度条的增长/缩短模式,实现"呼吸"效果。
与ProgressButton的集成
CircularProgressAnimatedDrawable与ProgressButton的集成主要通过以下方式实现:
- 回调机制:通过设置callback为ProgressButton,实现动画更新时的重绘触发
- 接口方法:ProgressButton接口定义了创建和管理进度动画的方法
- 状态同步:通过ProgressButton的状态管理,控制动画的开始和停止
在ProgressButton的实现中,通过createProgressDrawable()方法创建CircularProgressAnimatedDrawable实例:
internal fun ProgressButton.createProgressDrawable(): CircularProgressAnimatedDrawable = CircularProgressAnimatedDrawable(this, spinningBarWidth, spinningBarColor).apply { // 设置边界和回调 setBounds(left, top, right, bottom) callback = this@createProgressDrawable }然后在drawProgress()方法中控制动画的绘制:
internal fun CircularProgressAnimatedDrawable.drawProgress(canvas: Canvas) { if (isRunning) { draw(canvas) } else { start() } }使用与扩展建议
理解了CircularProgressAnimatedDrawable的实现机制后,开发者可以:
- 自定义视觉属性:通过修改borderWidth、arcColor等属性,定制进度条的外观
- 调整动画参数:修改ANGLE_ANIMATOR_DURATION和SWEEP_ANIMATOR_DURATION等常量,调整动画速度
- 扩展动画效果:通过继承或组合方式,实现更复杂的进度动画效果
- 优化性能:在不需要动画时及时调用stop()和dispose()方法,释放资源
例如,要修改进度条颜色,可以调用setLoadingBarColor()方法:
fun setLoadingBarColor(color: Int) { paint.color = color }要控制动画状态,可以使用start()和stop()方法:
override fun start() { if (isRunning) { return } indeterminateAnimator.start() } override fun stop() { if (!isRunning) { return } indeterminateAnimator.end() }总结
CircularProgressAnimatedDrawable通过巧妙的动画设计和与按钮组件的紧密集成,为LoadingButtonAndroid提供了核心的进度展示功能。其双动画协同工作的机制,既实现了平滑的旋转效果,又通过长度变化营造了"呼吸"感,为用户提供了直观的加载状态反馈。
通过深入理解这一实现,开发者不仅可以更好地使用LoadingButtonAndroid库,还可以借鉴其动画设计思路,应用到其他需要进度展示的场景中。无论是自定义进度条样式,还是优化动画性能,掌握CircularProgressAnimatedDrawable的工作原理都将大有裨益。
要开始使用这个库,你可以通过以下命令克隆仓库:
git clone https://gitcode.com/gh_mirrors/lo/LoadingButtonAndroid然后参考项目中的示例代码,快速将这一强大的按钮组件集成到你的Android应用中。
【免费下载链接】LoadingButtonAndroidA button to substitute the ProgressDialog项目地址: https://gitcode.com/gh_mirrors/lo/LoadingButtonAndroid
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考