Lottie源码分析

2023-05-16

简介

我们使用Lottie的时候,最关键的类就是LottieAnimationView(继承自ImageView)和LottieDrawable(继承自Drawable),Lottie的描述文件最终会解析成一系列的Layer,然后在绘制的时候,根据不同的进度,绘制Layer的不同帧。

JSON描述文件

在分析源码之前,我们需要先认识一下,加载的json 文件的数据结构是怎样的。
比较重要的有三层,
assets,这个描述的是,图片资源的位置;
layers,这个描述的是,每个图层的相关信息;
shapes,这个描述的是,具体图层的动画元素相关信息。
-w1057

assets会解析成LottieImageAsset对象,
Layers层在渲染的时候,会被解析成以下几种类型中的一种。
-w760
shapes会被解析成ShapeGroup

动画文件的加载

整体的时序图:

整体的类图:

Lottie提供了各种数据源获取的API,根据数据源的不同选择不同的获取方式。
-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);
    }
   });
  }

如上,先调用fromAssetFileName方法,会直接同步使用AssetManageropen方法,然后将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文件解析完成,接着会调用LottieDrawablesetComposition方法。进行一系列的初始化配置,包括速度、原始进度、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进行对应的绘制。
-w577
当调用animator.start的时候,LottieDrawableonAnimationUpdate就会被回调,根据动画的进度,调用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里面控制的。在这里会调用到每个BaseLayersetProgress,

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会触发LottieDrawableinvalidateSelf方法,进行重新绘制。随着动画的不断执行,就会不断绘制对应进度的样式,形成动画。

    public void onValueChanged() {
        this.invalidateSelf();
    }
    private void invalidateSelf() {
    this.lottieDrawable.invalidateSelf();
    }

小结

整个流程其实就是:
1、通过LottieComposition.Factory获取对应数据源的json文件并解析成LottieComposition
2、调用LottieDrawablesetComposition,将所有的图层解析成对应的Layer,并构建出一个基础的CompositionLayer.
3、接着调用LottieDrawable的动画执行方法,触发BaseLayer的draw()方法不断执行,不断的绘制各个图层从而形成动画。

参考文章

1、https://www.imooc.com/article/29249

2、https://www.jianshu.com/p/e41c7d826324

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Lottie源码分析 的相关文章

随机推荐

  • 这个交互也太炸裂了趴

    动画是为一个app创造出色用户体验的重要组成部分 它的关键挑战是向用户解释应用程序的逻辑 xff0c 但是常见的错误是鲁莽地使用动画 xff0c 从而否定了改善用户体验的整个观点 为了使应用出色而不仅仅是出色或平庸 xff0c 动画必须正确
  • 在ES6的语法中如何给数组去重

    这是从数组中筛选出重复项并仅返回唯一值的三种方法 我最喜欢使用Set xff0c 因为它最短 xff0c 最简单 xff1b set Set是ES6中引入的新数据对象 因为Set仅允许您存储唯一值 传递数组时 xff0c 它将删除所有重复值
  • 笔记啊啊啊啊

    判断某个字符串中是否其他某些字符串 span class token keyword const span deviceName span class token operator 61 span e span class token pu
  • android 高德地图之poi搜索功能的实现

    二话不多说 先看效果 这个功能我是用Fragmentdialog里面做的 也遇到不少坑 第一 就是设置背景的drawable为纯白色导致键盘弹出的时候 recyclerview的布局被顶上去导致出现白色布局 有点扎眼 最后改成了设置为和背景
  • android之 h5调用系统相机和相册并显示

    先上html界面的代码 放在assets里面就可以了 我也不太会html 所以随便写了点 span class hljs doctype lt doctype html gt span span class hljs tag lt span
  • 深坑之Webview,解决H5调用android相机拍照和录像

    最近在开发过程中遇到一个问题 主要是调用第三方的实名认证 需要拍照和录像 办过支付宝大宝卡和腾讯的大王卡的都知道这玩意 办卡的时候就需要进行实名认证 人脸识别 本来第三方平台 xxx流量公司 说的是直接用WebView加载这个H5界面就完事
  • rxjava2定时器每秒请求一次数据

    项目进行的过程中有个需求是在20秒内每秒请求一次数据 xff0c 请求成功的json中有个字段 xff0c 如果有这个字段代表请求成功 xff0c 如果没有则继续请求 xff0c 直到20秒结束 xff0c 20秒结束则失败 本来最开始采用
  • Flutter导航栏实现

    学了几天的flutter 似乎有点感觉了 今天来上手搞一个导航栏 实现类似android里面的ViewPager 43 Fragment的效果 二话不说直接上代码 import 39 package flutter material dar
  • SpringBoot 日志文件

    1 日志的作用2 日志怎么用3 自定义日志打印3 1 得到日志对象3 2 使用日志对象提供的方法打印日志3 3 日志格式说明 4 日志级别4 1 日志级别分类4 2 日志级别的配置 5 日志持久化6 更简单的实现自定义日志的打印6 1 准备
  • Flutter实现滑动头部折叠切换tab

    主要使用到NestedScrollView和SliverAppBar 先看效果 xff1a 代码如下 xff1a import 39 package flutter material dart 39 class ActPage extend
  • ubuntu apt-get 默认下载路径

    使用apt get install 命令时默认下载到 var cache apt archives路径下 xff0c sudo apt get clean命令可以删除该路径下的下载的deb包 例如仅下载ssh xff0c 不安装 xff1a
  • vscode 离线安装ssh

    首先打开官方插件地址 xff1a https marketplace visualstudio com VSCode 然后输入ssh 下载这两个插件 xff1a 安装这两个插件 xff1a 这样便在windows下安装成功了ssh 接下来需
  • windows BDA driver (abstract)

    AVStream is a Microsoft provided multimedia class driver that supports video only streaming and integrated audio video s
  • Ubuntu平台采用Qemu搭建ARM虚拟机环境

    1 Busybox编译 下载代码 xff1a 查看Busybox Source Control网站 xff0c 有代码控制说明 xff0c 我们采用git clone下载代码 xff1a BusyBox git clone git busy
  • 详细解读Python豆瓣电影Top250网页爬取(主要对re的运用&excel保存数据)//包括对库的简介

    python里面有很多操作都类似于c语言 xff0c 这里在爬取时主要需要注意用到的是for循环语句和各种库 个人认为python中主要还是对库的运用比较占大比例 xff08 这里的软件版本是PyCharm 2020 3 2 x64 xff
  • 在android中配置 slf4j + log4j 日志记录框架

    需求 xff1a 在项目开发中 xff0c 需要记录 操作日志 起初自己写了个简单的日志记录文本写入到文本的方法 xff0c 后来随着项目的膨胀 xff0c 需要考虑更多的操作 xff0c 开始考虑性能问题 实现 xff1a 考虑使用 sl
  • ZipInputStream解压远程文件报错,java.lang.IllegalArgumentException: MALFORMED[1]

    我遇到的问题是报的这个错java lang IllegalArgumentException MALFORMED 1 at java util zip ZipCoder toString ZipCoder java 65 不是 java l
  • OAuth2.0接百度平台进行授权

    百度开发文档 xff1a https openauth baidu com doc regdevelopers html 1 注册开发者账号并创建一个应用 2 创建应用后 xff0c 获取API Key和Secret Key 3 创建一个S
  • Spring 中最常用的 11 个扩展点

    1 自定义拦截器 spring mvc拦截器根spring拦截器相比 xff0c 它里面能够获取HttpServletRequest和HttpServletResponse等web对象实例 spring mvc拦截器的顶层接口是 xff1a
  • Lottie源码分析

    简介 我们使用Lottie的时候 xff0c 最关键的类就是LottieAnimationView 继承自ImageView 和LottieDrawable 继承自Drawable xff0c Lottie的描述文件最终会解析成一系列的La