Application summary of Lottie animation in the project

Application summary of Lottie animation in the project

1. Background

Based on the high-frequency usage of lottie animation, this paper summarizes the use skills and precautions of lottie animation in the project, which is convenient for dealing with similar needs in the project in the future, rapid retrieval and application, and will be improved from time to time in the later stage. You are welcome to supplement and correct the deficiencies.

2. What is Lottie?

Lottie is an open source animation framework that supports Android, iOS, React Native, Web and Windows devices at the same time.

Users only need to use it first Adobe After Effects The software makes animation, and then passes the animation file Bodymovin When exported as json files, you can parse these json files through Lottie to realize animation.

Front end developers only need to get the json file given by the UI (dynamic effect) students to realize 100% restoration of dynamic effect, which is simple and efficient.

3. Lottie core class

Lottie provides a lottieanimation view for users to use. In fact, the core of Lottie is LottieDrawable, which carries all the drawing work. Lottieanimation view encapsulates LottieDrawable and adds some functions such as parsing.

  • Lottieposition is the corresponding Model, which carries all the information.
  • CompositionLayer is a collection of layers.
  • ImageAssetBitmapManager is responsible for managing the image resources required for animation.
  • Lottieanimation view is the default and easiest way for users to load Lottie animation (directly)

4. Use steps

Open source address: https://github.com/airbnb/lottie-android

  1. Add dependency
dependencies {
  implementation 'com.airbnb.android:lottie:$lottieVersion'
}

Up to now, the latest version is: 4.2.1

  1. Use in layout xml:
<com.airbnb.lottie.LottieAnimationView
        android:id="@+id/lottieAnimationView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:lottie_fileName="hello-world.json"
        app:lottie_loop="true"
        app:lottie_rawRes="@raw/heart"
        app:lottie_imageAssetsFolder="images"
        app:lottie_autoPlay="true"

Note: the app here is Lottie_ rawRes,app:lottie_ The two properties filename are the same.

In the above figure, @ raw/heart in xml is a json file, and Lottie is implemented by parsing json files.

LottieAnimationView animationView = ... 
animationView.setAnimation(R.raw.hello_world); 
// or 
animationView.setAnimation(R.raw.hello_world.json); 
animationView.playAnimation();
  1. Control animation add animation monitor
lottieAnimationView.addAnimatorUpdateListener((animation) -> {
    // Animation status monitoring callback
});
lottieAnimationView.playAnimation(); // Play animation
...
if (lottieAnimationView.isAnimating()) { // Animation is running
}
...
// Progress range 0 ~ 1f, set animation progress
lottieAnimationView.setProgress(0.5f);
...
// Customize the animation duration. Here, use the ValueAnimator value animation to update the progress of AnimationView in real time to control the animation duration.
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f)
    .setDuration(500);
animator.addUpdateListener(animation -> {
    lottieAnimationView.setProgress(animation.getAnimatedValue());
});
animator.start();  // Start animation
...
lottieAnimationView.cancelAnimation();// Cancel animation

Therefore, you can freely control the frequency of animation, the playback progress of animation, various states of animation, etc

  1. Common methods:
methodfunction
setAnimation(int)Set the name of the json file to play the animation
setAnimation(int, CacheStrategy)Set json file resources and cache strategy for playing animation
loop(boolean)Sets whether the animation loops (false by default)
setRepeatMode(int)Sets the repetition mode of the animation (restart by default)
setRepeatCount(int)Sets the number of repetitions of the animation (default is - 1)
lottie_cacheStrategySet the cache policy for animation (the default is weak)
lottie_colorFilterSets the animated shading color (lowest priority)
setScale(float)Sets the scale of the animation (the default is 1f)
setProgress(float)Set the playback progress of the animation
setImageAssetsFolder(String)Set the image resource file address that the animation depends on
playAnimation()Play the animation from scratch
pauseAnimation()Pause animation
resumeAnimation()Continue playing animation from current position
cancelAnimation()Cancel animation playback
  1. Monitor animation progress [0,1]
   lottieAnimationView.addAnimatorUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {
            // Judge the end of animation loading
            if (valueAnimator.getAnimatedFraction() == 1f) {
            }
        }
    });
  1. Loop / play a part
lottieAnimationView.loop(true);
// Play a part of the animation
setMinFrame(...)
setMaxFrame(...)
setMinProgress(...)
setMaxProgress(...)
setMinAndMaxFrame(...)
setMinAndMaxProgress(...)
	4. Gets the duration of the animation
// For example, the duration of obtaining the animation (lottie_count_down) material given by the UI  
val lottieComposition: LottieComposition = LottieCompositionFactory.fromRawResSync(
                context,
                R.raw.lottie_count_down
            ).value!!
Log.d(TAG, "lottieComposition.duration : ${lottieComposition.duration} ")  //  Get 6000ms

5. Precautions for use

1. Control the speed and time of Lottie animation execution

After Lottie animation is exported to JSON, the speed and duration of animation execution have been fixed. Therefore, if you want to modify these two parameters, in addition to bothering the designer to use After Effects to modify them again and then export them, you can also use ValueAniamtor with setProgress() method.

2. Lottie's cache strategy

There may be some frequently used animations in the application, such as loading animations and so on. To avoid the overhead of loading files and serializing each time, you can set a caching strategy on your animation. In setAnimation() APIs, the optional second parameter CacheStrategy can be used.

By default, Lottie will save weak references to animation, which should be sufficient for most cases.

However, if you are sure that an animation will be used frequently, change its caching policy to cachestrategy Strong;
Or if you are sure that an animation is large and will not be used often, change the cache strategy to cachestrategy None.

CacheStrategy can be in three forms: None, weak, and Strong to let lottieanimation view use Strong or weak references to load and parse animation. Weak or Strong indicates the strength of the combined GC references in the cache.

6. Use examples in projects

Demand scenario:

When the user plays the countdown video, the smooth dynamic effect of the horizontal progress bar is realized synchronously according to the length of the video played.

Development and implementation ideas:

According to the animation (count_down.json) material provided by the UI, calculate the playback progress and speed at different times.

   <com.airbnb.lottie.LottieAnimationView
        android:id="@+id/lottieCountDownTime"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_gravity="center"
        app:lottie_fileName="lottie/count_down.json" />

Key ideas:

var mRestPastTime = 0L
private var countDownDuration: Long = 60L
fun start() {
        lottieCountDownTime.removeUpdateListener(mRestLottieAnimatorUpdateListener)
        lottieCountDownTime.cancelAnimation()
        mRestPastTime = 0L
        lottieCountDownTime.apply {
            val lottieComposition: LottieComposition = LottieCompositionFactory.fromRawResSync(
                context,
                R.raw.lottie_count_down
            ).value!!

            mRestLottieAnimatorUpdateListener = ValueAnimator.AnimatorUpdateListener { animation ->
                mRestPastTime = (countDownDuration * animation.animatedFraction).toLong()
                Log.d(TAG, "lottieComposition.duration : ${lottieComposition.duration} ")  // 5016ms
                countDownTime = countDownDuration - mRestPastTime
                val leftTime = (countDownTime / 1000).toString()
                Log.d(TAG, "leftTime:$leftTime ")  // 59 ~ 0 s
                setRestTimeText(countDownDuration - mRestPastTime, false)
                if (animation.animatedFraction >= 1) {
                    //Playback end processing
                }
            }
            setComposition(lottieComposition)
            progress = 0f
            //Core point: control the playback progress according to the duration
            speed = lottieComposition.duration / countDownDuration
            playAnimation()
            addAnimatorUpdateListener(mRestLottieAnimatorUpdateListener)
        }
    }

According to the above use examples, the dynamic effect of the horizontal progress bar given by the UI is realized, which is very smooth, and the sliding progress can correspond to the progress one by one according to different time lengths. In the countdown scene, if the user repeatedly pauses and starts the operation when playing the video, we have to deal with the problem of synchronization between the progress and the timing status. lottie animation is used here. You only need to call pause animation and resume animation.

Other implementation ideas 1:

If you use attribute animation to achieve, there may be different smooth or Caton situations.

Examples of using attribute Animation:

/**
     * Set the progress of ProgressBar (smooth growth)
     * @param progressBar progressBar
     * @param progressTo The value of 0 - 100 makes the smoothing effect smoother in 10 times of time
     */
    private fun setProgressSmooth(progressBar: ProgressBar, progressTo: Int) {
        val animator = ObjectAnimator.ofInt(
            progressBar, "progress",
            progressBar.progress,
            progressTo * 10
        )
        animator?.apply {
            duration = 300
            // Uniform drawing
            interpolator = LinearInterpolator()
            start()
        }
    }
  <ProgressBar
        android:id="@+id/countDownPb"
        style="@android:style/Widget.ProgressBar.Horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:max="1000"
        android:progressDrawable="@drawable/pb_pd_bg"
        tools:progress="500" />

Note: Pb_ pd_ bg. Implementation of background map in XML

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Progress bar background color -->
    <item android:id="@android:id/background">
        <shape>
            <solid android:color="@color/black" />
        </shape>
    </item>
    <!--  Second progress bar -->
    <item android:id="@android:id/progress">
        <clip>
            <shape>
                <solid android:color="@color/blue" />
            </shape>
        </clip>
    </item>
</layer-list>

Here, android:max="1000" is set to 1000. It should be noted that in normal development, we usually set it to 100. Here is a new idea. Zooming in 10 times when the progress is refreshed, combined with the linear interpolator feature of attribute animation, can basically achieve the effect of horizontal smoothness. However, when the user repeatedly pauses or resumes the countdown, due to the duration of attribute animation drawing time, it will not stop immediately, and there will be a sense of delay and pause.

Other development ideas 2:

You can use the idea of custom View to synchronize the progress when OnDraw refreshes. How to count down? If you don't want a smooth effect, you can 1s update the progress once,

If you want a smooth progress, update it once in 10ms. However, business logic such as suspension and resumption of countdown needs to be handled.

To sum up, two usage methods are summarized. In actual development, they are selected according to different usage scenarios. According to the current resource and dynamic efficiency requirements, lottile is the simplest and most efficient.

(I will further summarize later: the smooth progress in the round countdown, several ways to realize the countdown and the application of scenarios)

7. FQA

1, How to quickly preview a designer's lottie animation?

  1. UI designers provide Demo and dynamic Json+ HTML files. Click open the HTML file to preview.
  2. Online preview https://lottiefiles.com/private/animations After registering and logging in, directly put XX Drag the JSON dynamic file to the interface preview. The dynamic effect of some white may not be visible. It's best to confirm with the designer.
  3. Finally, preview in the code implementation.

2, Problem with loading carton

To be added

3, Set progress issues

setMinAndMaxProgress(0.5f, 1f)

After setting the progress: the speed here will accelerate

4, lottie animation size settings

Since the underlying layer of lottie is ImageView, it can only be solved by referring to similar properties.

If, set:

    <com.airbnb.lottie.LottieAnimationView
        android:id="@+id/countDownPb"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="fitXY"
        app:lottie_fileName="lottie/lottie_horizontal_progress.json" />

lottie_ horizontal_ progress. The size in JSON may not be covered with match_parent: android:scaleType="fitXY"

Here, you need to pay attention to the adapter. If the dynamic effect given by the UI is designed according to the fixed size, do not set this property here, otherwise the position will be disordered.

8. Future direction

May replace lottie with PAG

Compare PAG and lottie from the decoding and rendering level

9. References

lottie-android : github.com/airbnb/lott...

Introducing Lottie: medium.com/airbnb-engi...

design-lottie: airbnb.design/lottie/

bodymovin: github.com/bodymovin/b...

Animation: Jump-through: medium.com/google-deve...

Keywords: Java Android

Added by vishakh369 on Tue, 25 Jan 2022 14:44:17 +0200