代码之家  ›  专栏  ›  技术社区  ›  Quinn

设置多个视图的动画会导致动画冻结

  •  0
  • Quinn  · 技术社区  · 6 年前

    我有一个简单的动画,它使视图从屏幕的一侧转到另一侧,并在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>
    

    我想要的是让多个视图同时在屏幕上滑动,但速度不同,所以我还有两个XML调用 left_to_right_fast.xml left_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() 它按预期工作,条带在屏幕上滑动并重复。

    但我希望能同时有多个条纹,所以我试着打电话 多阿斯特普雷奇()) 连续三次,但当我这样做的时候,条纹似乎都是第一次在屏幕上显示出来,但是当它们重新出现时,它们不动了,静止几秒钟,然后消失,新的条纹似乎取代了它们。

    所以似乎动画是从 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?) {
            }
        })
    }
    

    更新: Here is a git repo with a demo project showing the bug

    你会注意到如果你只打一个电话 多阿斯特普雷奇()) 一旦它工作得很好,但不止一次-它对几个条纹很好,然后开始冻结动画。

    0 回复  |  直到 6 年前
        1
  •  1
  •   Skizo-ozᴉʞS ツ    6 年前

    你在做两个动画同时运行的时候做了一点小改动,但是我很好地跟踪了你的存储库,我测试了,是的,它看起来很滞后,问题是,如果你控制了 Animation A内 Handler 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 . 有关详细信息,请阅读此 When it's necessary to execute invalidate on a view

    这是一个简单的演示:

    https://vimeo.com/334493212

    这不是最好的选择,但是,好吧,它本身的问题根本没有正确的表述,所以,如果我有更多的时间,我会更深入地去检查它,但是从现在起,“冻结”的问题就消失了。