You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android如何创建类不确定进度条的重复动画移动渐变Drawable

解决移动渐变进度条的流畅性问题

你说得对,直接在onDraw里靠invalidate()驱动动画的方式确实不够可靠——当UI线程被其他任务(比如布局计算、触摸事件处理)占用时,invalidate()的调用会被推迟,导致动画卡顿、速度不稳定。改用Android官方的**动画API(ValueAnimator)**是更优的选择,它基于时间轴驱动,会和系统的帧同步机制(Choreographer)配合,即使丢帧也能保证动画进度和时间匹配,不会出现速度变慢的情况。

优化后的实现方案

我们可以用ValueAnimator来控制渐变的偏移比例,监听动画的进度更新来调整绘制偏移量,再触发重绘。这样动画的速度完全由时间控制,不受UI线程繁忙程度的影响:

class SmoothHorizontalProgressView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    private val loopDurationSeconds = 2.5f // 整个循环的时长(秒),控制动画快慢
    private var offsetRatio: Float = 0f // 偏移比例,从0到2(对应0到2倍视图宽度)
    private val rightDrawable: GradientDrawable = GradientDrawable().apply {
        gradientType = GradientDrawable.LINEAR_GRADIENT
        orientation = GradientDrawable.Orientation.LEFT_RIGHT
        shape = GradientDrawable.RECTANGLE
    }
    private val leftDrawable: GradientDrawable = GradientDrawable().apply {
        gradientType = GradientDrawable.LINEAR_GRADIENT
        orientation = GradientDrawable.Orientation.RIGHT_LEFT
        shape = GradientDrawable.RECTANGLE
    }
    private val animator: ValueAnimator by lazy {
        ValueAnimator.ofFloat(0f, 2f).apply {
            duration = (loopDurationSeconds * 1000).toLong()
            repeatCount = ValueAnimator.INFINITE
            repeatMode = ValueAnimator.RESTART
            interpolator = LinearInterpolator() // 保证渐变匀速移动
            addUpdateListener { animation ->
                offsetRatio = animation.animatedValue as Float
                invalidate() // 仅在动画进度更新时触发重绘
            }
        }
    }

    init {
        if (isInEditMode) setGradientColors(intArrayOf(Color.RED, Color.BLUE))
    }

    fun setGradientColors(colors: IntArray) {
        rightDrawable.colors = colors
        leftDrawable.colors = colors
        invalidate()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        rightDrawable.setBounds(0, 0, widthSize, heightSize)
        leftDrawable.setBounds(0, 0, widthSize, heightSize)
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        animator.start() // 视图附着到窗口时启动动画
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        animator.cancel() // 视图脱离窗口时停止动画,避免内存泄漏
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        canvas.save()
        val width = width.toFloat()
        val currentOffset = offsetRatio * width

        when {
            currentOffset < width -> {
                // 右渐变在当前偏移位置,左渐变在其左侧(偏移-宽度)
                canvas.translate(currentOffset, 0f)
                rightDrawable.draw(canvas)
                canvas.translate(-width, 0f)
                leftDrawable.draw(canvas)
            }
            else -> {
                // 偏移超过宽度后,左渐变在当前偏移-宽度的位置,右渐变在其左侧
                canvas.translate(currentOffset - width, 0f)
                leftDrawable.draw(canvas)
                canvas.translate(-width, 0f)
                rightDrawable.draw(canvas)
            }
        }
        canvas.restore()
    }
}

关键改进点说明

  1. 用ValueAnimator驱动动画:

    • 动画基于时间轴运行,时长固定(loopDurationSeconds控制整个循环的时间),即使UI线程繁忙,系统也会自动调整进度,保证动画完成时间一致。
    • 使用LinearInterpolator确保渐变匀速移动,避免速度忽快忽慢。
  2. 生命周期管理:

    • 在onAttachedToWindow启动动画,onDetachedFromWindow停止动画,避免视图销毁后动画继续运行导致内存泄漏。
  3. 更清晰的偏移逻辑:

    • 用offsetRatio(0到2)代替原来的xMovement,计算更直观,也更容易和动画API配合。

使用方式

和原来的用法一致,设置渐变颜色即可:

smoothHorizontalProgressView.setGradientColors(intArrayOf(Color.RED, Color.BLUE))

额外建议

如果你需要支持暂停/继续动画,可以添加对应的方法:

fun pauseAnimation() {
    if (animator.isRunning) animator.pause()
}

fun resumeAnimation() {
    if (animator.isPaused) animator.resume()
}

内容的提问来源于stack exchange,提问作者android developer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:40:27