使用LayoutAnimationController為RecyclerView添加動畫

使用LayoutAnimationController為RecyclerView添加動畫

@author:莫川

一、前言

為RecyclerView的Item添加動畫有很多中方案,比如通過設(shè)置setItemAnimator來實(shí)現(xiàn)或者是通過遍歷RecyclerView中的子View,然后分別對子View做動畫。今天介紹一種更加簡單的方式:通過LayoutAnimationController的方式,對RecyclerView的Item做動畫。

二、效果以及源碼

先看效果:首先是對LinearLayoutManager的RecyclerView。

  • 1.使用LayoutAnimationController的8種動畫的播放效果
img
  • 2.使用GridLayoutAnimationController的8種動畫的播放效果
img
img
Github源碼:https://github.com/nuptboyzhb/RecyclerViewAnimation

三、實(shí)現(xiàn)方案

(1)LayoutAnimationController

比如實(shí)現(xiàn)從依次從左側(cè)進(jìn)入的動畫效果,我們首先需要實(shí)現(xiàn)一個(gè)Item的動畫效果,然后創(chuàng)建一個(gè)LayoutAnimationController對象,并設(shè)置每一個(gè)item播放動畫的時(shí)間延時(shí)和item的播放順序。

以從左側(cè)進(jìn)入為例,每個(gè)單獨(dú)的Item的動畫如下:

  • 1.Item動畫

    ...
    /**
     * 從左側(cè)進(jìn)入,并帶有彈性的動畫
     *
     * @return
     */
    public static AnimationSet getAnimationSetFromLeft() {
        AnimationSet animationSet = new AnimationSet(true);
        TranslateAnimation translateX1 = new TranslateAnimation(RELATIVE_TO_SELF, -1.0f, RELATIVE_TO_SELF, 0.1f,
                RELATIVE_TO_SELF, 0, RELATIVE_TO_SELF, 0);
        translateX1.setDuration(300);
        translateX1.setInterpolator(new DecelerateInterpolator());
        translateX1.setStartOffset(0);

        TranslateAnimation translateX2 = new TranslateAnimation(RELATIVE_TO_SELF, 0.1f, RELATIVE_TO_SELF, -0.1f,
                RELATIVE_TO_SELF, 0, RELATIVE_TO_SELF, 0);
        translateX2.setStartOffset(300);
        translateX2.setInterpolator(new DecelerateInterpolator());
        translateX2.setDuration(50);

        TranslateAnimation translateX3 = new TranslateAnimation(RELATIVE_TO_SELF, -0.1f, RELATIVE_TO_SELF, 0f,
                RELATIVE_TO_SELF, 0, RELATIVE_TO_SELF, 0);
        translateX3.setStartOffset(350);
        translateX3.setInterpolator(new DecelerateInterpolator());
        translateX3.setDuration(50);

        animationSet.addAnimation(translateX1);
        animationSet.addAnimation(translateX2);
        animationSet.addAnimation(translateX3);
        animationSet.setDuration(400);

        return animationSet;
    }
    ...

為了讓Item看起來有‘彈性’效果,animationSet添加了三個(gè)移動動畫,分別是從左側(cè)進(jìn)入(-100%),移動到右側(cè)的10%,然后在從右側(cè)(10%)移動到左側(cè)(-10%),最后再從(-10%)移動到原本的位置(0%)。這樣就有了移動后的彈性效果。

  • 2.設(shè)置LayoutAnimationController的屬性

2.1 設(shè)置ViewGroup的子View播放動畫之間的offset。

/**
 * Sets the delay, as a fraction of the animation duration, by which the children's animations are offset.
 */
void setDelay(float delay)

2.2 設(shè)置ViewGroup的子View播放動畫的順序

/**
 * Sets the order used to compute the delay of each child's animation.
 */
void setOrder(int order)

setOrder可以取值為LayoutAnimationController.ORDER_NORMAL(正常順序),LayoutAnimationController.ORDER_RANDOM(隨機(jī)順序)以及LayoutAnimationController.ORDER_REVERSE(逆序)。這里的demo設(shè)置的是正常順序。

  • 3.播放動畫
   /**
     * 播放RecyclerView動畫
     *
     * @param animation
     * @param isReverse
     */
    public void playLayoutAnimation(Animation animation, boolean isReverse) {
        LayoutAnimationController controller = new LayoutAnimationController(animation);
        controller.setDelay(0.1f);
        controller.setOrder(isReverse ? LayoutAnimationController.ORDER_REVERSE : LayoutAnimationController.ORDER_NORMAL);

        mRecyclerView.setLayoutAnimation(controller);
        mRecyclerView.getAdapter().notifyDataSetChanged();
        mRecyclerView.scheduleLayoutAnimation();
    }

通過viewGroup.setLayoutAnimation設(shè)置layout動畫。然后通知ViewGroup重新繪制,調(diào)用scheduleLayoutAnimation方法播放動畫。

(2)GridLayoutAnimationController

上述方法針對的是線性的RecyclerView,也就是說RecyclerView的LayoutManager設(shè)置的是LinearLayoutManager.

    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_list);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        mRecyclerView.setAdapter(new DemoRecyclerViewAdapter());
    }
    ...

而對于使用GridLayoutManager和StaggeredGridLayoutManager的RecyclerView來說,我們需要使用GridLayoutAnimationController,其他步驟與LayoutAnimationController一致。

    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_grid);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        mRecyclerView = (StaggeredGridRecyclerView) findViewById(R.id.recycler_view);
        mRecyclerView.setLayoutManager(new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL));
        mStaggeredGridAdapter = new StaggeredGridAdapter();
        mStaggeredGridAdapter.setDataSet(mockData());
        mRecyclerView.setAdapter(mStaggeredGridAdapter);
    }

    ....

同樣的,仍然使用之前的Animation創(chuàng)建GridLayoutAnimationController。

   ...
   /**
     * 播放動畫
     *
     * @param animation
     * @param isReverse
     */
    public void playLayoutAnimation(Animation animation, boolean isReverse) {
        GridLayoutAnimationController controller = new GridLayoutAnimationController(animation);
        controller.setColumnDelay(0.2f);
        controller.setRowDelay(0.3f);
        controller.setOrder(isReverse ? LayoutAnimationController.ORDER_REVERSE : LayoutAnimationController.ORDER_NORMAL);

        mRecyclerView.setLayoutAnimation(controller);
        mRecyclerView.getAdapter().notifyDataSetChanged();
        mRecyclerView.scheduleLayoutAnimation();
    }
    ...

GridLayoutAnimationController的delay方法可以分別按照Column和Row維度進(jìn)行設(shè)置。

本以為到此順利結(jié)束。運(yùn)行后發(fā)現(xiàn),會Crash,log為:

...
E/AndroidRuntime( 7876): java.lang.ClassCastException: android.view.animation.LayoutAnimationController$AnimationParameters cannot be cast to android.view.animation.GridLayoutAnimationController$AnimationParameters
E/AndroidRuntime( 7876):    at android.view.animation.GridLayoutAnimationController.getDelayForView(GridLayoutAnimationController.java:299)
E/AndroidRuntime( 7876):    at android.view.animation.LayoutAnimationController.getAnimationForView(LayoutAnimationController.java:321)
E/AndroidRuntime( 7876):    at android.view.ViewGroup.bindLayoutAnimation(ViewGroup.java:4227)
E/AndroidRuntime( 7876):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3272)
E/AndroidRuntime( 7876):    at android.view.View.draw(View.java:15618)
E/AndroidRuntime( 7876):    at android.support.v7.widget.RecyclerView.draw(RecyclerView.java:3869)
E/AndroidRuntime( 7876):    at android.view.View.updateDisplayListIfDirty(View.java:14495)
E/AndroidRuntime( 7876):    at android.view.View.getDisplayList(View.java:14524)
E/AndroidRuntime( 7876):    at android.view.View.draw(View.java:15315)
E/AndroidRuntime( 7876):    at android.view.ViewGroup.drawChild(ViewGroup.java:3536)
E/AndroidRuntime( 7876):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3329)
E/AndroidRuntime( 7876):    at android.view.View.draw(View.java:15618)
E/AndroidRuntime( 7876):    at android.view.View.updateDisplayListIfDirty(View.java:14495)
E/AndroidRuntime( 7876):    at android.view.View.getDisplayList(View.java:14524)
...

為了解決這個(gè)問題,我們需要override RecyclerView的attachLayoutAnimationParameters方法:

package com.github.nuptboyzhb.recyclerviewanimation.grid;

import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.GridLayoutAnimationController;

/**
 * @version Created by haibozheng on 2016/12/9.
 * @Author Zheng Haibo
 * @Blog github.com/nuptboyzhb
 * @Company Alibaba Group
 * @Description StaggeredGridRecyclerView
 */
public class StaggeredGridRecyclerView extends RecyclerView {

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

    public StaggeredGridRecyclerView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public StaggeredGridRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * 支持GridLayoutManager以及StaggeredGridLayoutManager
     *
     * @param child
     * @param params
     * @param index
     * @param count
     */
    @Override
    protected void attachLayoutAnimationParameters(View child, ViewGroup.LayoutParams params,
                                                   int index, int count) {
        LayoutManager layoutManager = this.getLayoutManager();
        if (getAdapter() != null && (layoutManager instanceof GridLayoutManager
                || layoutManager instanceof StaggeredGridLayoutManager)) {

            GridLayoutAnimationController.AnimationParameters animationParams =
                    (GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters;

            if (animationParams == null) {
                animationParams = new GridLayoutAnimationController.AnimationParameters();
                params.layoutAnimationParameters = animationParams;
            }

            int columns = 0;
            if (layoutManager instanceof GridLayoutManager) {
                columns = ((GridLayoutManager) layoutManager).getSpanCount();
            } else {
                columns = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
            }

            animationParams.count = count;
            animationParams.index = index;
            animationParams.columnsCount = columns;
            animationParams.rowsCount = count / columns;

            final int invertedIndex = count - 1 - index;
            animationParams.column = columns - 1 - (invertedIndex % columns);
            animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns;

        } else {
            super.attachLayoutAnimationParameters(child, params, index, count);
        }
    }
}

更多動畫效果,請參見Github源碼:https://github.com/nuptboyzhb/RecyclerViewAnimation

四、總結(jié)

通過LayoutAnimationController或者GridLayoutAnimationController來實(shí)現(xiàn)RecyclerView的動畫,非常簡單,而且效果很好。該方式不僅可以應(yīng)用于RecyclerView,而且還適用于ListView、LinearLayout、GridView等ViewGroup。比如,如下是作用在一個(gè)LinearLayout的效果。


linearlayout_demo


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容