Lottie使用&源码分析
Posted 程序猿陈大发
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lottie使用&源码分析相关的知识,希望对你有一定的参考价值。
一、简介
Lottie是Airbnb开源的一个动画渲染库,支持多平台,包括ios、android、React Native以及Web。Lottie动画的制作流程如下:设计师通用After Effects 制作动画,然后通过Bodymovin插件导出对应的json文件,给到每个端的研发,然后进行编译展示。
二、简单使用
1、依赖引入
2、使用
在代码里面创建一个LottieAnimationView
对象,然后使用setAnimation
设置存放在assets
目录下的JSON文件,然后运行就可以看到对应的动画效果了。
public class LoadingView extends FrameLayout {
private LottieAnimationView lottieAnimationView;
public LoadingView(@NonNull Context context) {
super(context);
lottieAnimationView = new LottieAnimationView(getContext());
LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1000);
layoutParams.gravity = Gravity.CENTER;
addView(lottieAnimationView, layoutParams);
lottieAnimationView.setAnimation("LottieLogo1.json");
lottieAnimationView.setRepeatCount(ValueAnimator.INFINITE);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
lottieAnimationView.playAnimation();
}
}
这里有个小细节需要注意一点,如果遇到首次播放不了的情况,可以将playAnimation
的调用,放在onAttachedToWindow
的下面,具体原因后面解释。
三、进阶用法
多样的数据源
-
a、从raw文件夹获取: src/main/res/raw
; -
b、从 src/main/assets
加载zip文件; -
c、从一个指向json文件或者zip文件的url; -
d、从网络返回的json字符串; -
e、从 InputStream
获取到的json文件或者zip文件;带来的好处就是,根据根据诉求可以动态进行改变。
代码变色
lottie提供了三个改变颜色的API,
addColorFilter();
addColorFilterToLayer();
addColorFilterToContent();
-
addColorFilter 调用LottieAnimationView的 addColorFilter
方法,即可修改对应的颜色。
lottieAnimationView.addColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.SRC_ATOP));
譬如,官方demo 里面的HamburgerArrow.json
文件,原先是紫色的,增加上以上代码之后,整个就变成了红色。注意,这种修改方式,只能使用在背景透明的动画上,如果lottie动画本身就自带了背景色,那这种方式并不适用。因为addColorFilter
的实现原理是为每一层都加上了colorfilter,对应的源码如下:
@Override public void addColorFilter(@Nullable String layerName, @Nullable String contentName,
@Nullable ColorFilter colorFilter) {
for (int i = 0; i < layers.size(); ++i) {
final BaseLayer layer = layers.get(i);
final String name = layer.getLayerModel().getName();
if (layerName == null) {
layer.addColorFilter(null, null, colorFilter);
} else if (name.equals(layerName)) {
layer.addColorFilter(layerName, contentName, colorFilter);
}
}
}
会连背景色也被替换成为红色。导致看不出动画效果。
-
addColorFilterToLayer 为了解决上面提到的问题,lottie提供了另外一个api
addColorFilterToLayer
,可以在指定的layer上增加对应的colorfilter. -
addColorFilterToContent 进一步的,通过
addColorFilterToContent
可以给对应的layer的content,增加对应的色值。
setSpeed
设置播放速度,譬如如果设置了2,就是普通不设置场景下的2倍速度进行播放。
setProgress
设置开始播放的进度。第一次播放的时候,从整体进度的百分之多少开始执行动画,如果是loop的方式,只会影响第一次的进度。
设置动画监听
lottieAnimationView.addAnimatorListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
}
@Override
public void onAnimationCancel(Animator animation) {
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
lottieAnimationView.addAnimatorUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {}});
性能提升
1、Lottie动画如果包含遮罩、阴影或者蒙版,在性能会有所下降;如果必须使用遮罩,务必让其覆盖最小的区域。2.在列表中使用lottie动画,要使用缓存,避免内存抖动 3.开启硬件加速。默认是关闭的。
四、其他效率提升工具
五、源码分析
我们使用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<String, List<Layer>> precomps = new HashMap<>();
//图片资源
private final Map<String, LottieImageAsset> images = new HashMap<>();
//所有的Layer,带对应的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;
}
至此,就已经将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()方法不断执行,不断的绘制各个图层从而形成动画。
以上是关于Lottie使用&源码分析的主要内容,如果未能解决你的问题,请参考以下文章
音视频开发之旅(62) -Lottie 源码分析之json解析
音视频开发之旅(62) -Lottie 源码分析之json解析
音视频开发之旅(63) -Lottie 源码分析之动画与绘制
音视频开发之旅(63) -Lottie 源码分析之动画与绘制