簡介
我們使用Lottie
的時(shí)候红淡,最關(guān)鍵的類就是LottieAnimationView
(繼承自ImageView)和LottieDrawable
(繼承自Drawable)降铸,Lottie的描述文件最終會(huì)解析成一系列的Layer
推掸,然后在繪制的時(shí)候谅畅,根據(jù)不同的進(jìn)度,繪制Layer
的不同幀胜茧。
JSON描述文件
在分析源碼之前呻顽,我們需要先認(rèn)識(shí)一下丹墨,加載的json 文件的數(shù)據(jù)結(jié)構(gòu)是怎樣的廊遍。
比較重要的有三層,
assets
,這個(gè)描述的是贩挣,圖片資源的位置喉前;
layers
,這個(gè)描述的是,每個(gè)圖層的相關(guān)信息王财;
shapes
,這個(gè)描述的是卵迂,具體圖層的動(dòng)畫元素相關(guān)信息。
-w1057
assets
會(huì)解析成LottieImageAsset
對(duì)象搪搏,
Layers
層在渲染的時(shí)候狭握,會(huì)被解析成以下幾種類型中的一種疯溺。
-w760
shapes
會(huì)被解析成ShapeGroup
论颅。
動(dòng)畫文件的加載
整體的時(shí)序圖:
整體的類圖:
Lottie提供了各種數(shù)據(jù)源獲取的API,根據(jù)數(shù)據(jù)源的不同選擇不同的獲取方式囱嫩。
-w779
這里我們介紹一下恃疯,從assets下面獲取解析json文件的流程。
public void setAnimation(final String animationName, final CacheStrategy cacheStrategy) {
this.animationName = animationName;
lottieDrawable.cancelAnimation();
cancelLoaderTask();
compositionLoader = LottieComposition.Factory.fromAssetFileName(getContext(), animationName,
new OnCompositionLoadedListener() {
@Override public void onCompositionLoaded(LottieComposition composition) {
if (cacheStrategy == CacheStrategy.Strong) {
strongRefCache.put(animationName, composition);
} else if (cacheStrategy == CacheStrategy.Weak) {
weakRefCache.put(animationName, new WeakReference<>(composition));
}
setComposition(composition);
}
});
}
如上墨闲,先調(diào)用fromAssetFileName
方法今妄,會(huì)直接同步使用AssetManager
的open
方法,然后將InputStream
流提供給FileCompositionLoader
(繼承自AsyncTask),在里面進(jìn)行異步解析。最終會(huì)返回一個(gè)LottieComposition
對(duì)象盾鳞。
public class LottieComposition {
//預(yù)合成的圖層
private final Map<String, List<Layer>> precomps = new HashMap<>();
//圖片資源
private final Map<String, LottieImageAsset> images = new HashMap<>();
//所有的Layer犬性,帶對(duì)應(yīng)的ID
private final LongSparseArray<Layer> layerMap = new LongSparseArray<>();
//所有的layer,
private final List<Layer> layers = new ArrayList<>();
private final Rect bounds;
private final long startFrame;
private final long endFrame;
private final int frameRate;
private final float dpScale;
}
至此,就已經(jīng)將json文件解析完成腾仅,接著會(huì)調(diào)用LottieDrawable
的setComposition
方法乒裆。進(jìn)行一系列的初始化配置,包括速度推励、原始進(jìn)度鹤耍、colorFilter的設(shè)置、layer之間的關(guān)系等验辞。
public boolean setComposition(LottieComposition composition) {
if (this.composition == composition){return false;}
clearComposition();
this.composition = composition;
//設(shè)置速度
setSpeed(speed);
updateBounds();
//構(gòu)建CompositionLayer稿黄,會(huì)將所有的Layer轉(zhuǎn)換成為可以繪制的各種BaseLayer
buildCompositionLayer();
//處理colorFilter的設(shè)置
applyColorFilters();
//處理進(jìn)度的設(shè)置
setProgress(progress);
//如果需要,怎在以上配置完成跌造,開始執(zhí)行動(dòng)畫
if(playAnimationWhenCompositionAdded){
playAnimationWhenCompositionAdded = false;
playAnimation();
}
if(reverseAnimationWhenCompositionAdded) {
reverseAnimationWhenCompositionAdded = false;
reverseAnimation();
}
return true;
}
動(dòng)畫文件的渲染
其實(shí)就是將上面構(gòu)建出來的各種BaseLayer進(jìn)行對(duì)應(yīng)的繪制杆怕。
-w577
當(dāng)調(diào)用animator.start
的時(shí)候,LottieDrawable
的onAnimationUpdate
就會(huì)被回調(diào),根據(jù)動(dòng)畫的進(jìn)度鼻听,調(diào)用setProgress
方法進(jìn)行更新财著。
public void onAnimationUpdate(ValueAnimator animation) {
if (systemAnimationsAreDisabled) {
animator.cancel();
setProgress(1f);
} else {
setProgress((float) animation.getAnimatedValue());
}
}
public void setProgress(@FloatRange(from = 0f, to = 1f) float progress) {
this.progress = progress;
if (compositionLayer != null) {
compositionLayer.setProgress(progress);
}
}
而真正實(shí)現(xiàn)動(dòng)畫功能的,其實(shí)是CompositionLayer里面控制的撑碴。在這里會(huì)調(diào)用到每個(gè)BaseLayer
的setProgress
,
public void setProgress(@FloatRange(from = 0f, to = 1f) float progress) {
super.setProgress(progress);
progress -= layerModel.getStartProgress();
for (int i = layers.size() - 1; i >= 0; i--) {
layers.get(i).setProgress(progress);
}
}
而BaseLayer的setProgress
會(huì)觸發(fā)LottieDrawable
的invalidateSelf
方法撑教,進(jìn)行重新繪制。隨著動(dòng)畫的不斷執(zhí)行醉拓,就會(huì)不斷繪制對(duì)應(yīng)進(jìn)度的樣式伟姐,形成動(dòng)畫。
public void onValueChanged() {
this.invalidateSelf();
}
private void invalidateSelf() {
this.lottieDrawable.invalidateSelf();
}
小結(jié)
整個(gè)流程其實(shí)就是:
1亿卤、通過LottieComposition.Factory
獲取對(duì)應(yīng)數(shù)據(jù)源的json文件并解析成LottieComposition
愤兵。
2、調(diào)用LottieDrawable
的setComposition
排吴,將所有的圖層解析成對(duì)應(yīng)的Layer,并構(gòu)建出一個(gè)基礎(chǔ)的CompositionLayer
.
3秆乳、接著調(diào)用LottieDrawable
的動(dòng)畫執(zhí)行方法,觸發(fā)BaseLayer的draw()方法不斷執(zhí)行钻哩,不斷的繪制各個(gè)圖層從而形成動(dòng)畫屹堰。