LottieAndroid使用詳解及源碼解析—輕而易舉實現各種復雜動畫

LottieAndroid使用詳解及源碼解析,讓你的應用加載動畫變得輕而易舉。
看源碼的時候跟著我最下面的兩張時序圖慢慢走,一遍看不懂就再看一遍,別著急,多看幾遍就能懂了。雖然這里不是用的最新框架源碼,但是基本原理都是一致的,不影響你使用最新版本時候遇到問題,然后定位解決。

看懂源碼有什么用?當你使用這個框架遇到一些奇怪的Bug的時候,有時候不一定是你的問題,可能是源碼中的問題,追蹤源碼有利于你快速定位問題。我在使用的時候遇到過源碼的問題,后來在GitHub的issue中看到有人提了,再后來的版本就修復了。

為了寫好這篇文章,我花了一周多的時間,因為是給公司做分享,所以很多細節(jié)我是講出來的,并沒有寫出來,因為要寫的話會很多,但這篇文章基本上完全可以滿足你對Lottie的使用和對源碼的理解,接下來耐心看吧。

我們主要從以下四個方面來講解:

一、Lottie簡介

二、LottieAndroid的使用

三、LottieAndroid源碼解析

四、可能遇到的問題會有哪些


一、Lottie簡介

  • Lottie是什么?

    • Lottie是Airbnb開源的一個動畫渲染庫,同時支持Android、IOS、React Native和Web平臺,Lottie目前只支持渲染播放After Effects動畫。Lottie使用bobymovin(After Effects插件)到處的json數據作為動畫數據源。使用Lottie可以讓動畫顯示變得簡單方便。

    • 從動畫制作到動畫顯示流程如下:


    • 工作流程:

      1. 設計師使用Affer Effects制作動畫并導出json文件,參考:Lottie動畫社區(qū)
      2. 各端開發(fā)使用相應的LottieSDK實現動畫效果,GitHub下載地址:Lottie AndroidLottie IOS,React Native,Web
    • 注意事項:

      設計師同學制作各個平臺(Android、IOS、React Netive)動畫時需要查看Lottie在不同平臺支持的特性,否則制作出來的動畫顯示可能會有問題,設計同學制作動畫參考:不同平臺Lottie支持特性

  • 為什么要使用Lottie?

    • 先看看在沒有Lottie之前我們是怎么實現相對復雜動畫的:

      1. 使用GIF,占用空間大,有些動畫顯示效果不佳,需要適配分辨率,Android原生不支持GIF動畫的顯示。
      2. 使用幀動畫,占用空間大,依然會遇到不同分辨率適配的問題。
      3. 組合式動畫,通過大量代碼實現復雜的動畫效果。
    • 使用Lottie可以解決的問題:

      1. 降低動畫設計和開發(fā)成本
      2. 解決設計提供動畫效果與實現不一致問題
      3. 占用空間更小
      4. 不同的手機分辨率不需要適配
  • Lottie適用于哪些場景?

    • 啟動(splash)動畫:典型場景是APP logo動畫的播放
    • 上下拉刷新(refresh)動畫:所有APP都必備的功能,利用 Lottie 可以做的更加簡單酷炫了
    • 加載(loading)動畫:典型場景是網絡請求的loading動畫
    • 提示(tips)動畫:典型場景是空白頁的提示
    • 按鈕(button)動畫:典型場景如switch按鈕、編輯按鈕、播放按鈕等按鈕的
    • 禮物(gift)動畫:典型場景是直播類APP的高級動畫播放
  • 我們想要使用Lottie替代哪些動畫?

    • 首先并不是在APP中所有的動畫都要用Lottie來替換
    • 一些可以通過屬性動畫來實現的簡單動畫就不需要用Lottie來實現了
    • 替代一些通過代碼不好實現的動畫效果
    • 替代GIF動畫和幀動畫

二、LottieAndroid的使用

  • 集成到項目中(以2.2.0版本為例)

    • 添加依賴:compile 'com.airbnb.android:lottie:2.2.0'

      Lottie版本號參考:Maven庫查看Lottie各版本號

    • Gradle依賴修改:

      最低版本:MIN_SDK_VERSION = 16
      編譯版本:COMPILE_SDK_VERSION = 25
      所有的兼容包需要升級到版本號為25.3.1

        compile 'com.android.support:appcompat-v7:25.3.1'
        compile 'com.android.support:cardview-v7:25.3.1'
        compile 'com.android.support:design:25.3.1'
        compile 'com.android.support:recyclerview-v7:25.3.1'
        compile 'com.android.support:palette-v7:25.3.1'
        compile 'com.android.support:support-v4:25.3.1'
      
    • 如何查看Lottie各版本需要對應的AndroidSDK編譯版本和兼容包版本號?

      以Lottie2.2.0版本為例:mvnrepository lottie2.2.0

  • 使用方法

    • Lottie基本用法查看官方文檔

      1. 首先將json文件放到assets文件夾下:

        可以直接放到assets目錄下,或者在assets目錄下創(chuàng)建一個二級目錄放在二級目錄下。
      2. 在布局中添加LottieAnimationView控件:

         <com.airbnb.lottie.LottieAnimationView
              android:id="@+id/animation_view"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              app:lottie_fileName="hello-world.json"
              app:lottie_loop="true"
              app:lottie_autoPlay="true" />
              
         如上圖,如果json文件在assets子文件夾中,lottie_fileName="lottieani/stars.json"
        

        LottieAnimationView可以設置的屬性如下:
      3. 得到LottieAnimationView對象進行動畫操作:

         LottieAnimationView animationView = (LottieAnimationView)findViewById(R.id.animation_view);
         // 布局中不指定文件可以在此設置,路徑設置同布局文件
         animationView.setAnimation("hello-world.json");
         // 是否循環(huán)播放
         animationView.loop(true);
         // 設置播放速率,例如:2代表播放速率是不設置時的二倍
         animationView.setSpeed(2f);
         // 開始播放
         animationView.playAnimation();
         
         // 暫停播放
         animationView.pauseAnimation();
         // 取消播放
         animationVIew.cancelAnimation();
         
         // 設置播放進度
         animationView.setProgress(0.5f);
         // 判斷是否正在播放
         animationView.isAnimating();
        

        setAnimation()有三種方法,可以直接設置動畫的Json對象,或者設置Json文件相對路徑名,且支持設置緩存類型:

        playAnimation()有三種方法:

        支持三種緩存類型,不緩存、弱類型和強類型:
      4. 添加動畫監(jiān)聽

         mAnimationView.addAnimatorListener(new Animator.AnimatorListener() {
             @Override
             public void onAnimationStart(Animator animation) {
                 Log.d(TAG, "onAnimationStart : " + animation.getDuration());
             }
        
             @Override
             public void onAnimationEnd(Animator animation) {
                 Log.d(TAG, "onAnimationEnd");
             }
        
             @Override
             public void onAnimationCancel(Animator animation) {
                 Log.d(TAG, "onAnimationCancel");
             }
        
             @Override
             public void onAnimationRepeat(Animator animation) {
                 Log.d(TAG, "onAnimationRepeat");
             }
         });
        
         mAnimationView.addAnimatorUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 //Log.d(TAG, "onAnimationUpdate : " + animation.getCurrentPlayTime());
             }
         });
        
    • 其他用法

      1. 自定義動畫的速率和時長

         ValueAnimator valueAnimator = ValueAnimator
                 .ofFloat(0f, 1f)
                 .setDuration(5000);
         valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
             @Override
             public void onAnimationUpdate(ValueAnimator animation) {
                 mAnimationView.setProgress((Float) animation.getAnimatedValue());
             }
         });
         valueAnimator.start();
        
      2. 給整個動畫添加一個特定圖層,或者一個圖層的特定內容添加一個顏色過濾器

         // 任何符合顏色過濾界面的類
         final PorterDuffColorFilter colorFilter = new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.LIGHTEN);
         
         // 在整個視圖中添加一個顏色過濾器
         animationView.addColorFilter(colorFilter);
         
         //在特定的圖層中添加一個顏色濾鏡
         animationView.addColorFilterToLayer("hello_layer", colorFilter);
         
         // 添加一個彩色過濾器特效“hello_layer”上的內容
         animationView.addColorFilterToContent("hello_layer", "hello", colorFilter);
         
         // 清除所有的顏色濾鏡
         animationView.clearColorFilters();
        
      3. 在列表中添加動畫,每個item循環(huán)播放該動畫

        如果按照下面寫法會有什么問題?如果按照下面寫法,即便是已經設置了lottieViewLive.loop(true)循環(huán)播放,item被劃走再劃回來動畫依舊會停止播放。

         <com.airbnb.lottie.LottieAnimationView
             android:id="@+id/lottieViewLive"
             android:layout_width="30dp"
             android:layout_height="30dp"/>
        
         // 在viewHolder中initView的時候開始動畫
         LottieAnimationView lottieViewLive = (LottieAnimationView) layout.findViewById(R.id.lottieViewLive);
         lottieViewLive.setAnimation("lottiejson/living.json");
         // 循環(huán)播放動畫
         lottieViewLive.loop(true);
         lottieViewLive.playAnimation();         
        

        為了解決上訴問題,只需要在布局中添加,屬性app:lottie_autoPlay="true";可以直接按照寫法在xml布局中添加動畫即可。為什么會這樣?待會在源碼解析部分講解。

         <com.airbnb.lottie.LottieAnimationView
             android:id="@+id/lottieViewLive"
             android:layout_width="30dp"
             android:layout_height="30dp"
             app:lottie_autoPlay="true"
             app:lottie_fileName="lottiejson/living.json"
             app:lottie_loop="true"/>
        
      4. 從網絡直接獲取json數據顯示動畫(Lottie本身不提供網絡請求)

        為什么要使用下面方式,而不是從網絡獲取json數據后直接animationView.setAnimation(json)。主要是為了使用OnCompositionLoadedListener,防止從網絡獲取的json數據錯誤解析出來的composition為null。

         LottieComposition.Factory.fromJson(getResources(), jsonObject, new OnCompositionLoadedListener() {
             @Override
             public void onCompositionLoaded(@Nullable LottieComposition composition) {
                 if (composition != null) {
                     mAnimationView.setComposition(composition);
                     mAnimationView.playAnimation();
                 } else {
                     // showErrorView();
                 }
             }
         });
        
      5. 加載含有圖片的復雜文件

        有些動畫比較復雜不是簡單的線條、圖塊所能實現的,這樣的動畫通過AE插件bobymovin生成的文件除了有一個json文件外,還會有一些png圖片。如何加載這樣的動畫呢?

        ① 加載本地含有圖片的動畫資源,json文件放在assets文件夾下,生成的png圖片同樣放在assets文件夾下(或者自己在assets文件夾下創(chuàng)建一個子目錄,假如叫l(wèi)ottieimage)。
         // 代碼這樣寫,需要設置圖片的文件夾地址
         mAnimationView.setImageAssetsFolder("lottieweaccept/images");
         mAnimationView.setAnimation("lottieweaccept/WeAccept.json");
         mAnimationView.playAnimation();
         
         // 或者在布局文件中添加這句代碼,指定圖片路徑
         app:lottie_imageAssetsFolder="lottieweaccept/images"
        

        ② 加載從網絡獲取的含有圖片的動畫資源,首先需要將資源下載到手機SD卡中
         final String absolutePath = imagesDir.getAbsolutePath();
         mAnimationView.setImageAssetDelegate(new ImageAssetDelegate() {
             @Override
             public Bitmap fetchBitmap(LottieImageAsset asset) {
                 return BitmapFactory.decodeFile(absolutePath + File.separator + asset.getFileName());
             }
         });
         LottieComposition.Factory.fromInputStream(LottieViewActivity.this, inputStream, new OnCompositionLoadedListener() {
             @Override
             public void onCompositionLoaded(@Nullable LottieComposition composition) {
                 if (composition != null) {
                     mAnimationView.setComposition(composition);
                     mAnimationView.playAnimation();
                 } else {
                     // doOtherThing();
                 }
             }
         });     
        

三、源碼解析

  • 從Json到動畫顯示的實現思路

    • 將復雜的圖片使用圖層表示,每一層表示不同的內容

    • 根據動畫需求可以只針對某一層做相應的平移、旋轉、縮放等動畫

    • Json文件中數據轉成LottieComposition數據對象,LottieDrawable負責將數據繪制成drawable,LottieAnimationView負責將LottieDrawable顯示出來。LottieAnimationView繼承自AppCompatImageView,LottieDrawable繼承自Drawable。
  • 先看看生成的Json數據,參考這里:Lottie:讓動畫如此簡單

  • 如何加載json數據并顯示圖像的?

    • animationView.setAnimation("hello-world.json");

      通過setAnimation()來看看,上面json數據到顯示圖像的過程。源碼時序圖如下(以2.2.0版本為例):
      1. LottieAnimationView初始化的時候會首先創(chuàng)建LottieDrawable對象,private final LottieDrawable lottieDrawable = new LottieDrawable()。init(),進行初始化的時候,解析xml設置的屬性。

      2. setAnimation(String fileName),加載Json文件。

      3. 通過異步加載,最終會調用到fromJsonSync()對Json文件進行解析。

      4. 解析結果通過onCompositionLoaded()回調到主線程,然后會將Json數據轉換成LottieComposition對象。

      5. lottieDrawable.setComposition(),將LottieComposition對象設置給LottieDrawable。

      6. 生成CompositionLayer對象,這個對象就是每一個圖層的對象。

      7. 通過setImageDrawable(lottieDrawable)將圖像顯示出來,顯示第一幀動畫。

  • 動畫如何運行起來的?

    • animationView.playAnimation();

      調用playAnimation()動畫是如何動起來的,源碼時序圖如下(以2.2.0版本為例):
      1. ValueAnimator實現的控制動畫進度。

      2. setProgress實現的顯示具體進度動畫。

      3. LottieDrawable中的draw繪制圖像,Canvas進行繪制。

  • 為什么在列表中加載動畫時設置了循環(huán)播放,item劃走再劃回來動畫還是會停止播放?為什么添加app:lottie_autoPlay="true"才行?
  • 我們來看看這兩句代碼,有沒有什么問題?

      // 異步加載json文件
      animationView.setAnimation("hello-world.json");
      animationView.playAnimation();
      animationView.setSpeed(2f);
    

    setAnimation是異步加載json文件的,調用setAnimation之后直接調用playAnimation()源碼是如何保證加載完數據之后再開始動畫呢?最終會走到LottieDrawable類中,如下:

性能問題

  • 性能和內存

    • 如果沒有mask和mattes,那么性能和內存非常好,沒有bitmap創(chuàng)建,大部分操作都是簡單的cavas繪制。
    • 如果存在mattes,將會創(chuàng)建2~3個bitmap。bitmap在動畫加載到window時被創(chuàng)建,被window刪除時回收。所以不宜在RecyclerView中使用包涵mattes或者mask的動畫,否則會引起bitmap抖動。除了內存抖動,mattes和mask中必要的bitmap.eraseColor()和canvas.drawBitmap()也會降低動畫性能。對于簡單的動畫,在實際使用時性能不太明顯。
    • 如果在列表中使用動畫,推薦使用緩存LottieAnimationView.setAnimation(String, CacheStrategy) 。

全屏適配參考


更新:添加2.5.1版本時遇到的問題

1、 編譯版本需要 27
2、所有的suport包版本需要27.1.0

  compile 'com.android.support:appcompat-v7:27.1.0'

3、如果有引入lifecycle,版本需要1.1.0

  android.arch.lifecycle:extensions:1.1.0
  android.arch.lifecycle:compiler:1.1.0

如果項目中引入了livedata并且lifecycle版本號不對,會報如下錯誤:
Error:Program type already present: android.arch.lifecycle.LiveData
參考:https://stackoverflow.com/questions/49056723/errorprogram-type-already-present-android-arch-lifecycle-livedata
com.firebaseui:firebase-ui-firestore:3.1.0 depends on android.arch.lifecycle:extensions:1.0.0-beta1. Switching to version 3.2.2 fixes the issue by using the Lifecycle 1.1 libraries that Support Library 27.1.0 are built upon.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 【Android 動畫】 動畫分類補間動畫(Tween動畫)幀動畫(Frame 動畫)屬性動畫(Property ...
    Rtia閱讀 6,442評論 1 38
  • 1 背景 不能只分析源碼呀,分析的同時也要整理歸納基礎知識,剛好有人微博私信讓全面說說Android的動畫,所以今...
    未聞椛洺閱讀 2,861評論 0 10
  • 前沿 該文章主要介紹了 Lottie是什么,如何為 Lottie 制作動畫,以及 Lottie的應用場景。適合設計...
    York_魚閱讀 22,986評論 8 171
  • Lottie框架個人操作簡述步驟:1.下載Creative Cloud,這個是adobe公司專用的下載器,若之前有...
    PeterGQ閱讀 1,362評論 2 2
  • 文|蘇小橙 當我們有了孩子以后,我們總是小心翼翼亦步亦趨的跟隨在他的身后,這樣就能在他跌倒或者走錯的時候,然后快速...
    蘇小塵102閱讀 994評論 2 1

友情鏈接更多精彩內容