Android 帶Lottie動畫的導航欄

2020-09-16 12:00:18
  1. 什麼是Lottie動畫
    Lottie 是Airbnb開源的一個面向 iOS、Android、React Native 的動畫庫,能分析 Adobe After Effects 匯出的動畫,並且能讓原生 App 像使用靜態素材一樣使用這些動畫,完美實現動畫效果,動畫效果比原生動畫要優美很多。Lottie動畫由UI提供,UI給到我們的是xxx.json檔案,這個檔案就是動畫檔案。
    優點:動畫效果好,靈活,匯入方便,使用簡單,可以從網路下載,支援多平臺。
    缺點:效能沒有屬性動畫好。

  2. 匯入Lottie動畫庫
    github地址
    在app build.gradle 中匯入
    implementation 'com.airbnb.android:lottie:$lottieVersion'
    $lottieVersion 為版本號。
    注意2.8.0及之後的版本加入了android x, 如果你的專案沒有使用android x ,要使用2.8.0之前的版本,否則會編譯失敗

Lottie 2.8.0 and above only supports projects that have been migrated
to androidx.

  1. 匯入動畫檔案
    在 main資料夾下建立assets資料夾,將json檔案放入assets資料夾下。需要確認json中是否包含本地檔案路徑,例如 img0/image1.png,如果存在,需要將本地圖片按路徑儲存,否則執行會報錯。
    在這裡插入圖片描述
  2. 程式碼中實現動畫播放
    關鍵類 LottieAnimationView ,LottieDrawable 。此類實現Lottie動畫的設定及控制。
    LottieAnimationView繼承AppCompatImageView,所以它是支援Lottie的Imageview。
mLottieView.setImageAssetsFolder("image0"); //設定本地檔案路徑
mLottieView.setRepeatCount(0); //設定重複次數,預設0
mLottieView.setAnimation(mAnimationPath);//mAnimationPath 是動畫json檔案的相對路徑
mLottieView.playAnimation();//播放動畫  
//其他屬性自行搜尋

以上設定也可以在xml中設定。

app:lottie_fileName="xxx.json"
app:lottie_repeatCount="0"
app:lottie_imageAssetsFolder="image0"//其他屬性自行搜尋
  1. 導航欄實現
    新建自定義控制元件 LottieTabView
public class LottieTabView extends FrameLayout {
    private int mTextNormalColor;
    private int mTextSelectColor;
    private float mTextSize;
    private String mTabName;
    private Drawable mIconNormal;
    private String mAnimationPath;
    private LottieAnimationView mLottieView;
    private TextView mTabNameView;
    private TextView mMsgView;
    private boolean isSelected;

    public LottieTabView(Context context) {
        super(context);
    }

    public LottieTabView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public LottieTabView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }


    private void init(Context context, AttributeSet attrs) {
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LottieTabView);
        mTextNormalColor = ta.getColor(R.styleable.LottieTabView_text_normal_color, Color.BLACK);
        mTextSelectColor = ta.getColor(R.styleable.LottieTabView_text_selected_color, Color.BLUE);
        mTextSize = ta.getDimension(R.styleable.LottieTabView_text_size, SizeUtils.dp2px(5));
        mIconNormal = ta.getDrawable(R.styleable.LottieTabView_icon_normal);
        mAnimationPath = ta.getString(R.styleable.LottieTabView_lottie_path);
        mTabName = ta.getString(R.styleable.LottieTabView_tab_name);
        isSelected = ta.getBoolean(R.styleable.LottieTabView_tab_selected, false);
        ta.recycle();
        initView(context);
    }

    private void initView(Context context) {
        View containView = LayoutInflater.from(context).inflate(R.layout.lottie_tab_view, null, false);
        mLottieView = containView.findViewById(R.id.animation_view);
        mLottieView.setRepeatCount(0);
        mTabNameView = containView.findViewById(R.id.tab_name);
        mTabNameView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
        mTabNameView.setTextColor(mTextNormalColor);
        mTabNameView.setText(mTabName);
        mMsgView = containView.findViewById(R.id.msg_view);
        this.addView(containView);
        if (isSelected) {
            selected();
        } else {
            unSelected();
        }
    }

    public void selected() {
        if (TextUtils.isEmpty(mAnimationPath)) {
            throw new NullPointerException("ainmation path must be not empty");
        } else {
            mLottieView.setAnimation(mAnimationPath);
            mLottieView.playAnimation();
            mTabNameView.setTextColor(mTextSelectColor);
        }
    }

    public void unSelected() {
        mTabNameView.setTextColor(mTextNormalColor);
        mLottieView.clearAnimation();
        mLottieView.setImageDrawable(mIconNormal);
    }
	//在右上角顯示訊息提示數量
    public void showMsg(int num) {
        if (num > 0 && num <= 99) {
            mMsgView.setVisibility(VISIBLE);
            mMsgView.setText(num + "");
        } else if (num > 99) {
            mMsgView.setVisibility(VISIBLE);
            mMsgView.setText("99+");
        } else {
            mMsgView.setVisibility(View.GONE);
        }
    }

控制元件佈局檔案

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:clipToPadding="false"
    android:clipChildren="false">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:gravity="center"
        android:orientation="vertical">

        <com.airbnb.lottie.LottieAnimationView
            android:id="@+id/animation_view"
            android:layout_width="21dp"
            android:layout_height="23dp" />

        <TextView
            android:id="@+id/tab_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="3dp"
            android:text="首頁"
            android:textSize="9sp"
            android:textColor="@color/colorMain" />
    </LinearLayout>

        <TextView
            android:id="@+id/msg_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginLeft="10dp"
            android:layout_marginBottom="30dp"
            android:background="@drawable/rc_unread_count_bg"
            android:gravity="center"
            android:textColor="@color/white"
            android:textSize="8sp"
            android:visibility="gone" />

</FrameLayout>

attr.xml中

 <declare-styleable name="LottieTabView">
        <attr name="text_selected_color" format="color"/>
        <attr name="text_normal_color" format="color"/>
        <attr name="text_size" format="dimension"/>
        <attr name="lottie_path" format="string"/>
        <attr name="icon_normal" format="reference"></attr>
        <attr name="tab_selected" format="boolean"></attr>
        <attr name="tab_name" format="string"/>
    </declare-styleable>

介面中使用

        <com.xxx.xxxx.view.widget.LottieTabView
            android:id="@+id/tab_view_main"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            app:icon_normal="@drawable/ic_main_normal"
            app:lottie_path="mainpage.json"
            app:tab_name="首頁"
            app:tab_selected="true"
            app:text_normal_color="@color/color66"
            app:text_selected_color="@color/colorMain"
            app:text_size="9sp" />

        <com.xxx.xxxx.view.widget.LottieTabView
            android:id="@+id/tab_view_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            app:icon_normal="@drawable/ic_msg_normal"
            app:lottie_path="news.json"
            app:tab_name="訊息"
            app:tab_selected="false"
            app:text_normal_color="@color/color66"
            app:text_selected_color="@color/colorMain"
            app:text_size="9sp" />

        <com..xxx.xxxx.view.widget.LottieTabView
            android:id="@+id/tab_view_deal"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            app:icon_normal="@drawable/ic_deal_normal"
            app:lottie_path="trade.json"
            app:tab_name="交易"
            app:tab_selected="false"
            app:text_normal_color="@color/color66"
            app:text_selected_color="@color/colorMain"
            app:text_size="9sp" />

        <com.xxx.xxxx.view.widget.LottieTabView
            android:id="@+id/tab_view_mine"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            app:icon_normal="@drawable/ic_mine_normal"
            app:lottie_path="user.json"
            app:tab_name="我的"
            app:tab_selected="false"
            app:text_normal_color="@color/color66"
            app:text_selected_color="@color/colorMain"
            app:text_size="9sp" />
    </LinearLayout>

Mainactivity中

@OnClick({R.id.tab_view_main, R.id.tab_view_msg, R.id.tab_view_deal, R.id.tab_view_mine})
    public void onClickView(View view) {
        switch (view.getId()) {
            case R.id.tab_view_main:
                displayFragment(0);//展示相應的fragment
                mLottieMainTab.selected();
                mLottieMsgTab.unSelected();
                mLottieDealTab.unSelected();
                mLottieMineTab.unSelected();
                break;
            case R.id.tab_view_msg:
                displayFragment(1);
                mLottieMsgTab.selected();
                mLottieDealTab.unSelected();
                mLottieMineTab.unSelected();
                mLottieMainTab.unSelected();
                break;
            case R.id.tab_view_deal:
                displayFragment(2);
                mLottieDealTab.selected();
                mLottieMsgTab.unSelected();
                mLottieMineTab.unSelected();
                mLottieMainTab.unSelected();
                break;
            case R.id.tab_view_mine:
                displayFragment(3);
                mLottieMineTab.selected();
                mLottieMsgTab.unSelected();
                mLottieDealTab.unSelected();
                mLottieMainTab.unSelected();
                break;
        }
    }

待優化:在此基礎上再封裝一層LottieTabLayout,動態設定每個LottieTabView的屬性。在MainActivity 中直接使用LottieTabLayout。

  1. 效果
    在這裡插入圖片描述
    視訊錄製有些卡頓,現實沒有問題。
    所需資源
    demo git