简介
我们使用Lottie
的时候,最关键的类就是LottieAnimationView
(继承自ImageView)和LottieDrawable
(继承自Drawable),Lottie的描述文件最终会解析成一系列的Layer
,然后在绘制的时候,根据不同的进度,绘制Layer
的不同帧。
JSON描述文件
在分析源码之前,我们需要先认识一下,加载的json 文件的数据结构是怎样的。
比较重要的有三层,
assets
,这个描述的是,图片资源的位置;
layers
,这个描述的是,每个图层的相关信息;
shapes
,这个描述的是,具体图层的动画元素相关信息。
assets
会解析成LottieImageAsset
对象,
Layers
层在渲染的时候,会被解析成以下几种类型中的一种。
shapes
会被解析成ShapeGroup
。
动画文件的加载
整体的时序图:
整体的类图:
Lottie提供了各种数据源获取的API,根据数据源的不同选择不同的获取方式。
这里我们介绍一下,从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);
}
});
}
如上,先调用fromAssetFileName
方法,会直接同步使用AssetManager
的open
方法,然后将InputStream
流提供给FileCompositionLoader
(继承自AsyncTask),在里面进行异步解析。最终会返回一个LottieComposition
对象。
public class LottieComposition {
//预合成的图层
private final Map> precomps = new HashMap<>();
//图片资源
private final Map images = new HashMap<>();
//所有的Layer,带对应的ID
private final LongSparseArray layerMap = new LongSparseArray<>();
//所有的layer,
private final List layers = new ArrayList<>();
private final Rect bounds;
private final long startFrame;
private final long endFrame;
private final int frameRate;
private final float dpScale;
}
至此,就已经将json文件解析完成,接着会调用LottieDrawable
的setComposition
方法。进行一系列的初始化配置,包括速度、原始进度、colorFilter的设置、layer之间的关系等。
public boolean setComposition(LottieComposition composition) {
if (this.composition == composition){return false;}
clearComposition();
this.composition = composition;
//设置速度
setSpeed(speed);
updateBounds();
//构建CompositionLayer,会将所有的Layer转换成为可以绘制的各种BaseLayer
buildCompositionLayer();
//处理colorFilter的设置
applyColorFilters();
//处理进度的设置
setProgress(progress);
//如果需要,怎在以上配置完成,开始执行动画
if(playAnimationWhenCompositionAdded){
playAnimationWhenCompositionAdded = false;
playAnimation();
}
if(reverseAnimationWhenCompositionAdded) {
reverseAnimationWhenCompositionAdded = false;
reverseAnimation();
}
return true;
}
动画文件的渲染
其实就是将上面构建出来的各种BaseLayer进行对应的绘制。
当调用animator.start
的时候,LottieDrawable
的onAnimationUpdate
就会被回调,根据动画的进度,调用setProgress
方法进行更新。
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);
}
}
而真正实现动画功能的,其实是CompositionLayer里面控制的。在这里会调用到每个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
会触发LottieDrawable
的invalidateSelf
方法,进行重新绘制。随着动画的不断执行,就会不断绘制对应进度的样式,形成动画。
public void onValueChanged() {
this.invalidateSelf();
}
private void invalidateSelf() {
this.lottieDrawable.invalidateSelf();
}
小结
整个流程其实就是:
1、通过LottieComposition.Factory
获取对应数据源的json文件并解析成LottieComposition
。
2、调用LottieDrawable
的setComposition
,将所有的图层解析成对应的Layer,并构建出一个基础的CompositionLayer
.
3、接着调用LottieDrawable
的动画执行方法,触发BaseLayer的draw()方法不断执行,不断的绘制各个图层从而形成动画。
参考文章
1、https://www.imooc.com/article/29249
2、https://www.jianshu.com/p/e41c7d826324