Lottie - 动画的使用

一、前言:

Lottie 是Airbnb开源的一个面向 iOS、Android、React Native 的动画库,能分析 Adobe After Effects 导出的动画,并且能让原生 App 像使用静态素材一样使用这些动画,完美实现动画效果。

现在使用各平台的 native 代码实现一套复杂的动画是一件很困难并且耗时的事,我们需要为不同尺寸的屏幕加载不同的素材资源,还需要写大量难维护的代码,而Lottie可以做到同一个动画文件在不同平台上实现相同的效果,极大减少开发时间,实现不同的动画,只需要设置不同的动画文件即可,极大减少开发和维护成本。

Lottie 地址:https://github.com/airbnb/lottie-android

Lottie 官网:https://airbnb.design/lottie/

1、为什么使用Lottie?

  1. 跨平台只有制作一套json动画文件便可以跨平台在 Android ios ReactNeative上使用,lottie库负责解析json文件并播放动画

  2. 可以支持网络下载json文件,本地播放,实时更新动画资源。

  3. 运行时效率上仅仅用Canvas去draw而已,流畅度非常棒,所以哪怕在Listview里去大量显示,内存占用和绘图效率都远远高于帧动画。

  4. 实现效果可以按设计出的100%还原到产品中。

  5. 开发周期大大减少。

二、使用:

1、在项目 的 build.gradle 文件添加依赖

dependencies {
     //lottie框架(现在的最新版本)
    implementation'com.airbnb.android:lottie:3.4.1'
}

注意:版本过老,会报错

2、 添加 Adobe After Effects 导出的动画文件

Lottie默认读取Assets中的文件,我们需要把设计导出的动画文件.json 保存在app/src/main/assets文件里。

位置.png

比如p_start.json中包含本地图片,则在assets中新建images文件夹,将图片放入即可


图片.png

3、 使用Lottie

  1. 直接布局加载*.json文件的方法:
  <com.airbnb.lottie.LottieAnimationView
            android:id="@+id/animation_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            app:lottie_fileName="app_loading.json"
            app:lottie_loop="true" />

lottie_fileName表示本地Assets文件中存的json动画文件

lottie_loop表示动画循环执行

2、代码加载动画方法

<com.airbnb.lottie.LottieAnimationView
            android:id="@+id/animation_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:lottie_fileName="app_loading.json"
            app:lottie_loop="true"
            app:lottie_autoPlay="true" />

lottie_autoPlay表示设置是否自动启动播放

        //声明控件
        lottieAnimationView = findViewById(R.id.animation_view);
        //这个可有可无,如果不涉及本地图片做动画可忽略
       // lottieAnimationView.setImageAssetsFolder("images");
        //设置动画文件
        lottieAnimationView.setAnimation("app_loading.json");
        //是否循环执行
        lottieAnimationView.loop(true);
        //执行动画
        lottieAnimationView.playAnimation();

  1. 加载网络.json文件
private void loadUrl(String url) {
        Request request = new Request.Builder().url(url).build();
        OkHttpClient client = new OkHttpClient();
        client.newCall(request).enqueue(new Callback() {
            @Override public void onFailure(Call call, IOException e) {}
            @Override public void onResponse(Call call, Response response) throws IOException {
                try {
                    JSONObject json = new JSONObject(response.body().string());
                    LottieComposition.Factory
                            .fromJson(getResources(), json, new OnCompositionLoadedListener() {
                                @Override
                                public void onCompositionLoaded(LottieComposition composition) {
                                    lottieAnimationView.setComposition(composition);
                                    lottieAnimationView.playAnimation();
                                }
                            });
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        });
    }

  1. 使用包含本地图片的lottie动画
    XML使用:
   <com.airbnb.lottie.LottieAnimationView
                android:id="@+id/lottie_view"
                android:layout_width="@dimen/dp_70"
                android:layout_height="@dimen/dp_50"
                app:lottie_fileName="p_start.json"
                app:lottie_imageAssetsFolder="images/"
                android:layout_below="@+id/tv_level1"
                android:layout_centerHorizontal="true"
                android:layout_marginTop="@dimen/dp_6"
                />

java代码:

   //动画1
    private fun initAnible() {
        //这个可有可无,如果不涉及本地图片做动画可忽略
        lottie_view.setImageAssetsFolder("images/");
        //设置动画文件
        lottie_view.setAnimation("p_start.json");
        //是否循环执行
        //lottie_view.loop(true);
        lottie_view.setOnClickListener {
            //执行动画
            lottie_view.playAnimation()
        }
    }

三、 常用方法

  • animationView.isAnimating();  动画是否在播放
  • animationView.playAnimation();    播放动画
  • animationView.pauseAnimation();   暂停动画
  • animationView.cancelAnimation();  取消动画
  • animationView.setProgress(progress);  设置进度,progress范围0~1
  • animationView.setMinAndMaxProgress(min,max);  设置播放范围,0~1
  • 监听动画进度
  • 设置动画硬件加速
  • 缓存动画
  • Lottie本身在 Android 平台已经做了适配工作,而且适配原理很简单,解析时,从 读取宽高之后会再乘以手机的密度。再在使用的时候判断适配后的宽高是否超过屏幕的宽高,如果超过则再进行缩放。以此保障 Lottie 在 Android 平台的显示效果

四、自定义刷新头

public class CustomClassicsHeader extends LinearLayout implements RefreshHeader {

    private TextView mHeaderText;//标题文本
//    private ImageView mArrowView;//下拉箭头
//    private ImageView mProgressView;//刷新动画视图
//    private ProgressDrawable mProgressDrawable;//刷新动画
    private LottieAnimationView lottieAnimationView;//加载动画
    private   View view;
    public CustomClassicsHeader(Context context) {
        this(context, null);
    }

    public CustomClassicsHeader(Context context, AttributeSet attrs) {
        super(context, attrs, 0);

         view = LayoutInflater.from(context).inflate(R.layout.item_custom_header, this);
//        setGravity(Gravity.CENTER);
//        mHeaderText = new TextView(context);
//        mProgressDrawable = new ProgressDrawable();
////        mArrowView = new ImageView(context);
////        mProgressView = new ImageView(context);
//        lottieAnimationView = new LottieAnimationView(context);
//        //mProgressView.setImageDrawable(mProgressDrawable);
//        //mArrowView.setImageDrawable(new ArrowDrawable());
//
//        mHeaderText.setTextColor(context.getResources().getColor(R.color.color_FF333333));
////        addView(mProgressView, SmartUtil.dp2px(20), SmartUtil.dp2px(20));
////        addView(mArrowView, SmartUtil.dp2px(20), SmartUtil.dp2px(20));
//        addView(lottieAnimationView, SmartUtil.dp2px(25), SmartUtil.dp2px(25));
//        addView(new Space(context), SmartUtil.dp2px(20), SmartUtil.dp2px(20));
//        addView(mHeaderText, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
//        setMinimumHeight(SmartUtil.dp2px(60));
//        //加载动画
//        lottieAnimationView.setAnimation("app_pull_refresh.json");
//        //是否循环执行
//        lottieAnimationView.loop(true);


        lottieAnimationView = view.findViewById(R.id.lottieAnimationView);
        mHeaderText = view.findViewById(R.id.tv_head);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

    @NonNull
    public View getView() {
        return view;//真实的视图就是自己,不能返回null
    }

    @NonNull
    @Override
    public SpinnerStyle getSpinnerStyle() {
        return SpinnerStyle.Translate;//指定为平移,不能null
    }

    @Override
    public void onStartAnimator(@NonNull RefreshLayout layout, int height, int maxDragHeight) {
       // mProgressDrawable.start();//开始动画
        //执行动画
        lottieAnimationView.playAnimation();
    }

    @Override
    public int onFinish(@NonNull RefreshLayout layout, boolean success) {
//        mProgressDrawable.stop();//停止动画
//        mProgressView.setVisibility(GONE);//隐藏动画
        lottieAnimationView.cancelAnimation();
        if (success) {
            mHeaderText.setText("刷新完成");
        } else {
            mHeaderText.setText("刷新失败");
        }
        return 500;//延迟500毫秒之后再弹回
    }

    @Override
    public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) {
        refreshLayout.setPrimaryColorsId(R.color.color_FFF96857,R.color.color_FFF96857);//全局设置主题颜色
        switch (newState) {
            case None:
            case PullDownToRefresh:
                mHeaderText.setText("下拉开始刷新");
//                mArrowView.setVisibility(VISIBLE);//显示下拉箭头
//                mProgressView.setVisibility(GONE);//隐藏动画
//                mArrowView.animate().rotation(0);//还原箭头方向

                break;
            case Refreshing:
                mHeaderText.setText("正在刷新");
//                mProgressView.setVisibility(VISIBLE);//显示加载动画
//                mArrowView.setVisibility(GONE);//隐藏箭头
                break;
            case ReleaseToRefresh:
                mHeaderText.setText("释放立即刷新");
               // mArrowView.animate().rotation(180);//显示箭头改为朝上
                break;
        }
    }

    @Override
    public void setPrimaryColors(int... colors) {

    }

    @Override
    public void onInitialized(@NonNull RefreshKernel kernel, int height, int maxDragHeight) {

    }

    /**
     * 移动
     * @param isDragging
     * @param percent
     * @param offset
     * @param height
     * @param maxDragHeight
     */
    @Override
    public void onMoving(boolean isDragging, float percent, int offset, int height, int maxDragHeight) {
        if(isDragging){
            //设置进度
            lottieAnimationView.setProgress(percent);
        }

    }

    @Override
    public void onReleased(@NonNull RefreshLayout refreshLayout, int height, int maxDragHeight) {

    }

    @Override
    public void onHorizontalDrag(float percentX, int offsetX, int offsetMax) {

    }

    @Override
    public boolean isSupportHorizontalDrag() {
        return false;
    }
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="@dimen/dp_70"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:background="@color/color_FFFFFF"
    >
    <com.airbnb.lottie.LottieAnimationView
        android:id="@+id/lottieAnimationView"
        android:layout_width="@dimen/dp_35"
        android:layout_height="@dimen/dp_35"
        android:layout_marginTop="@dimen/dp_10"
        app:lottie_fileName="app_pull_refresh.json"
        app:lottie_loop="true"
        app:lottie_autoPlay="true"
        android:layout_centerHorizontal="true"
        />

    <TextView
        android:id="@+id/tv_head"
        android:layout_marginTop="@dimen/dp_3"
        android:layout_below="@+id/lottieAnimationView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@color/color_FFA9A9A9"
        android:textSize="@dimen/sp_11"
        android:layout_centerHorizontal="true"
        android:text="下拉开始刷新"
        />
</RelativeLayout>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,919评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,567评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,316评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,294评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,318评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,245评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,120评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,964评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,376评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,592评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,764评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,460评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,070评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,697评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,846评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,819评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,665评论 2 354