Sequence / Chaining of animations in One LottieAnimationView
Asked Answered
H

4

8

I've received multiple animation files that should be played in sequence using Lottie library

What I'm currently trying to do is to reuse same LottieAnimationView and just set next animation when first one finishes. Code looks like this:

private fun playFirstAnimation() {
    binding.animation.setAnimation(R.raw.progress_1)
    binding.animation.repeatCount = 2
    binding.animation.addAnimatorListener(object : Animator.AnimatorListener {
        override fun onAnimationRepeat(animation: Animator?) = Unit
        override fun onAnimationCancel(animation: Animator?) = Unit
        override fun onAnimationStart(animation: Animator?) = Unit

        override fun onAnimationEnd(animation: Animator?) {
            binding.animation.removeAnimatorListener(this)
            notifyFirstAnimationFinished()
            playSecondAnimation()
        }
    })
    binding.animation.playAnimation()
}

private fun playSecondAnimation() {
    binding.animation.setAnimation(R.raw.progress_2)
    binding.animation.repeatCount = 0
    binding.animation.addAnimatorListener(object : Animator.AnimatorListener {
        override fun onAnimationRepeat(animation: Animator?) = Unit
        override fun onAnimationCancel(animation: Animator?) = Unit
        override fun onAnimationStart(animation: Animator?) = Unit

        override fun onAnimationEnd(animation: Animator?) {
            binding.animation.removeAnimatorListener(this)
            notifySecondAnimationFinished()
            playThirdAnimation()
        }
    })
    binding.animation.playAnimation()
}

The problem here (apart from ugly code) is that I get a small flicker (animation dissapears totally and nothing is displayed in it's location) when changing animations for a short time. I can't have all the animations merged into one, since I need to know when exactly one animation finished. And I really wouldn't want to have a separate LottieAnimationView for each animation and replacing them on every change as I might have many animations.

What options do I have in this situation?

Hacking answered 19/7, 2019 at 5:24 Comment(0)
H
9

Turns out there's quite a simple solution to this problem. Instead of calling setAnimation we can call setComposition and provide preloaded results from LottieCompositionFactory.fromRawResSync, LottieCompositionFactory.fromRawRes or any other method which is appropriate to your needs.

Hacking answered 19/7, 2019 at 16:12 Comment(1)
Can you please provide a code example?Swam
H
0

The issue is that once an animation is ended, you're trying to load another resource to the same View, which is a heavy process to do and at the same moment you're playing the animation. One creepy solution is to add a delay to it.

lottieView.setAnimation("firstlottie.json");
lottieView.playAnimation();
lottieView.addAnimatorListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animator) {

        }

        @Override
        public void onAnimationEnd(Animator animator) {
            //first animation has ended
            lottieView.pauseAnimation();
            lottieView.setAnimation("secondlottie.json"); //will show only the first frame

            new Handler().postDelayed(new Runnable() {
                 @Override
                 public void run() {
                      lottieView.playAnimation();
                }
             }, 500);
        }

        @Override
        public void onAnimationCancel(Animator animator) {

        }

        @Override
        public void onAnimationRepeat(Animator animator) {

        }
    });
Hyphen answered 19/7, 2019 at 5:47 Comment(3)
This still produces a flickerHacking
@Hacking try adding some more delay, then update meHyphen
I've tried that. This does not work. Every call to setAnimation produces flicker, then automatically updates the drawable.Hacking
P
0

I changed the setAnimation to setComposition so that it preloads the lottie files before actually showing them.

so instead of:

setAnimation(R.raw.animation)

use:

LottieCompositionFactory.fromRawResSync(requireContext(), R.raw.animation)?.let { result ->
                                result.value?.let { composition -> setComposition(composition) }
                            }

That stopped the flickering for me. I also used an AnimationListener for the loop.

 private fun setLottieAnimation(lottieView: LottieAnimationView) {
            lottieView.apply {
                var firstAnimation = true
                val loopListener: Animator.AnimatorListener = object : AnimatorListenerAdapter() {
                    override fun onAnimationRepeat(animation: Animator?) {
                        if (firstAnimation) {
                            firstAnimation = false
                            LottieCompositionFactory.fromRawResSync(requireContext(), R.raw.animation_2)?.let { result ->
                                result.value?.let { it -> setComposition(it) }
                            }
                            
                            repeatCount = LottieDrawable.INFINITE
                            playAnimation()
                        }
                    }
                }
    
                LottieCompositionFactory.fromRawResSync(requireContext(), R.raw.animation_1)?.let { result ->
                    result.value?.let { it -> setComposition(it) }
                }
              
                repeatCount = 1
                addAnimatorListener(loopListener)
            }
        }
Pig answered 6/9, 2022 at 9:53 Comment(0)
K
0

I have added this code and its working fine with me (Recursive call)

call from another location. playMyAnimation(1,"file_1.json")

private fun playMyAnimation(count: Int, animFile: String) {
    binding.defaultScreen.animationView.setAnimation(animFile)
    binding.defaultScreen.animationView.speed = 1f

    binding.defaultScreen.animationView.addAnimatorListener(object : Animator.AnimatorListener{
        override fun onAnimationStart(animation: Animator) =Unit

        override fun onAnimationEnd(animation: Animator) {
            binding.defaultScreen.animationView.removeAllAnimatorListeners()
            if(count==1){
                playMyAnimation(2,"file_2.json")
            }else if(count==2){
                playMyAnimation(3,"file_3.json")
            }
        }

        override fun onAnimationCancel(animation: Animator) =Unit

        override fun onAnimationRepeat(animation: Animator) =Unit

    })
    binding.defaultScreen.animationView.playAnimation()
}
Kingbolt answered 14/9, 2023 at 7:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.