首页 > 解决方案 > 为多个视图设置动画会导致动画冻结

问题描述

我有一个简单的动画,使视图从屏幕的一侧转到另一侧,并在 anim 文件夹中定义为left_to_right.xml

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true">

    <translate
        android:fromXDelta="-300%" android:toXDelta="360%"
        android:fromYDelta="0%" android:toYDelta="0%"
        android:duration="3200"/>

</set>

我想要的是让多个视图同时在屏幕上滑动,但速度不同,所以我又调用了 2 个 xml left_to_right_fast.xmlleft_to_right_slow.xml它们完全相同,只是持续时间不同。

所以在我的视图类中,我有这些方法来创建条纹图像并为其设置动画,当动画完成后,我将其删除并制作另一个:

    private fun doAStripe() {
        if (!isRunning) return

        val stripe = makeStripe()

        stripeHolder.addView(stripe)

        stripe.animation.onAnimationEnd {
            (stripe.parent as ViewGroup).removeView(stripe)

            doAStripe()
        }

        stripe.animate()
    }

    private fun makeStripe(): AppCompatImageView {
        val imageView = AppCompatImageView(context)

        imageView.layoutParams = LayoutParams(WRAP_CONTENT, WRAP_CONTENT)

        imageView.setImageResource(listOf(R.drawable.cp_stripe_blue, R.drawable.cp_stripe_gray, R.drawable.cp_stripe_red).random())

        imageView.clearAnimation()

        imageView.animation = AnimationUtils.loadAnimation(context, listOf(R.anim.left_to_right, R.anim.left_to_right_fast, R.anim.left_to_right_slow).random())

        imageView.x = width / 2f
        imageView.y = (0..(stripeHolder.height)).random().toFloat()

        return imageView
    }

因此,当我调用doAStripe()它按预期工作时,一条条纹滑过屏幕并重复。

但是我希望能够同时出现多个条纹,所以我尝试doAStripe()连续调用 3 次,但是当我这样做时 - 第一次条纹似乎都在屏幕上显示动画,但是当它们重新出现时它们不会不动,静坐几秒钟然后消失,新的条纹出现代替它们。

所以看起来动画正在发生,因为onAnimationEnd被调用了......但它实际上并没有发生。有谁知道这是什么原因?

onAnimationEnd的也是这个方便的扩展:

fun Animation.onAnimationEnd(callback: () -> Unit) {
    setAnimationListener(object : Animation.AnimationListener {
        override fun onAnimationRepeat(p0: Animation?) {
        }

        override fun onAnimationEnd(p0: Animation?) {
            callback()
        }

        override fun onAnimationStart(p0: Animation?) {
        }
    })
}

更新:这是一个带有演示项目的 git 存储库,其中显示了该错误

你会注意到,如果你只doAStripe()在它运行良好时调用 onCreate,但不止一次调用它 - 它适用于几个条纹然后开始冻结动画。

标签: androidxmlanimationkotlin

解决方案


你做了一些小技巧来让两个动画同时运行,但是我遵循了你的存储库并进行了测试,是的,它看起来很滞后,问题是,如果你控制Animation内部 aHandler Android将为你处理线程,并且您的实现存在一个问题,所有这些动画都在MainThread,这会导致滞后。

所以我最终这样做了:

private fun doAStripe() {
    val stripe = makeStripe()
    stripeHolder.addView(stripe)
    stripe.postDelayed({
        (stripe.parent as ViewGroup).removeView(stripe)
        doAStripe()
    }, stripe.animation.duration)
    stripe.animate().withLayer()
}

它看起来很冰沙,但速度并不快,也许你可以用anim文件来控制它。

我还添加了withLayer()

它为您随后添加到动画的所有视图启用硬件动画。

还添加imageView.invalidate()到您的makeStripe()方法中,以使ImageView. 有关更多信息,您可以阅读此当需要对视图执行无效时

这是一个简单的演示它的外观:

https://vimeo.com/334493212

这不是最好的选择,但是,它本身的问题根本没有正确说明,所以,如果我有更多时间,我会更深入地检查它,但从现在开始,“冻结”的问题是走了。


推荐阅读