1

I have a simple animation that makes a view go from one side of the screen to the other and its defined in the anim folder as 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>

What I want is to have multiple views sliding across the screen at the same time but different speeds, so I have 2 more xmls called left_to_right_fast.xml and left_to_right_slow.xml which are the exact same except for a different duration.

So in my view class I have these methods to create a image of a stripe and animate it, and when the animation is done, I remove it and make another:

    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
    }

So when I just call doAStripe() it works as expected, a stripe slides across the screen, and repeats.

But I want to be able to have multiple stripes going at the same time, so I try calling doAStripe() three times in a row, but when I do that - the stripes all seem to animate across the screen the first time but when the reappear they don't move, sit still for a couple seconds then disappears and new stripes appear to replace them.

So it seems like the animation is happening since the onAnimationEnd is getting called... but its not actually happening. Anyone know the reason for this?

Also my onAnimationEnd is just this convenience extension:

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?) {
        }
    })
}

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

you'll notice if you make onCreate only call doAStripe() once it works great, but call it more than once - and it works well for a couple stripes then starts freezing the animations.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Quinn
  • 7,072
  • 7
  • 39
  • 61
  • what exactly you want to do is that having three view with animation that go left and right with different speed ? right ? but you want it to get repeat ? or you also want ? a pause between animation ? – Arbaz Pirwani May 03 '19 at 18:23
  • @ArbazPirwani I want three views going at the same time, and each of them repeating with no pause in-between. – Quinn May 03 '19 at 18:25
  • 1
    A sample on Github would be great so we can test your real code... – Skizo-ozᴉʞS ツ May 06 '19 at 15:20
  • @Skizo-ozᴉʞS unfortunatley the real code is in a giant private repo for my work and I'm not allowed to make it public. Perhaps ill make a demo app shortly and give a link to that. – Quinn May 06 '19 at 15:42
  • @Skizo-ozᴉʞS Just made a git repo which has an example of the bug, updated the question – Quinn May 06 '19 at 16:01
  • Why are you calling `doAStripe()` twice? – Skizo-ozᴉʞS ツ May 06 '19 at 17:09
  • Also you are not using isRunning, I mean you are not setting to true or false never – Skizo-ozᴉʞS ツ May 06 '19 at 17:14
  • I'm calling it twice because I want more than 1 stripe going at a time. and yes isRunning is not used in the example app, it was used in the app I copied this code from but wasn't relevant to the question. – Quinn May 06 '19 at 17:33

1 Answers1

2

You do a little hack to do the two animations run at the same time, but well I followed your repository and I tested and yes, it looks laggy, the thing is, if you control the Animation inside a Handler Android will treat the threading for you, and there is one problem with your implementation, all of those animation were on the MainThread and this causes laggy.

So I ended up doing this :

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

It looks smoothie, but not much fast, maybe you can control it with the anim files.

I also added withLayer()

it enables hardware animations for all views you subsequently add to the animation.

Also added imageView.invalidate() in your makeStripe() method, to invalidate the ImageView. For more information you can read this When it's necessary to execute invalidate on a view

This is a simple demo how it looks :

https://vimeo.com/334493212

This is not the best option, but, well, the problem it self is not correctly stated at all, so, if I have more time I'll go deeper to check it out, but from now, the problem of "freeze" is gone.

Skizo-ozᴉʞS ツ
  • 19,464
  • 18
  • 81
  • 148