Android Kotlin – reset position after animation

I’m making a swipe animation, this is inside onCreate:

val distance = swipe.left.toFloat()

anim = swipe.animate().rotationBy(-30f).setDuration(1000) // SWIPE TO LEFT
    .translationX(-distance)
    .setInterpolator(AccelerateDecelerateInterpolator())

anim!!.setListener(object : Animator.AnimatorListener {
    override fun onAnimationRepeat(animation: Animator?) {}
    override fun onAnimationCancel(animation: Animator?) {}
    override fun onAnimationStart(animation: Animator?) {}

    override fun onAnimationEnd(animation: Animator?) {
        swipe.animate().rotationBy(30f).setDuration(300)
            .translationX(distance)
            .setInterpolator(AccelerateDecelerateInterpolator()).start() // RESET THE POSITION BY SWIPING BACK TO RIGHT

        Log.d("pikaboo", "wth")
    }
})

anim!!.start()

As you can see I try to reset it inside onAnimationEnd but then I get wth printed out multiple times per second and the swipe view disappears!

What is wrong here? How can I reset and repeat the animation?

Answer

When you call animate on a View, it returns the ViewPropertyAnimator instance associated with that Viewwhich is a singleton, i.e. there’s only one and you get that back each time you call animate

This class is not constructed by the caller, but rather by the View whose properties it will animate. Calls to View.animate() will return a reference to the appropriate ViewPropertyAnimator object for that View.

So you call animate and save the ViewPropertyAnimator result as anim, and then set the animation listener on that. Your listener has its onAnimationEnd function, which starts a new animation on the same view, which means it’s using the same ViewPropertyAnimator (anim), which has the listener set which starts a new animation when this one ends… see where I’m going here? You got yourself a never ending loop!

Your best bet is probably to use a one-shot end animation, which you can do with that fluent animate syntax anyway! No need for a listener. Try this:

anim = swipe.animate().rotationBy(-30f).setDuration(1000) // SWIPE TO LEFT
    .translationX(-distance)
    .setInterpolator(AccelerateDecelerateInterpolator())
    .withEndAction {
        swipe.animate().rotationBy(30f).setDuration(300)
            .translationX(distance)
        
       .setInterpolator(AccelerateDecelerateInterpolator()).start()
    }

Can’t fix the formatting on there (for a site about programming it’s really hard to work with code in these answers) but yeah – you could clean it up by making some of it into a function anyway (since it’s the same thing, just with negative values)


Also you should probably make anim a lateinit var instead of nullable and doing !! everywhere, that’s a bad sign and it always causes troubles in the end!