Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> RecyclerView.ItemAnimator終極解讀(三)--繼承DefaultItemAnimator實現自定義動畫

RecyclerView.ItemAnimator終極解讀(三)--繼承DefaultItemAnimator實現自定義動畫

編輯:關於Android編程

DefaultItemAnimator是Android OS中一個默認的RecyclerView動畫實現類,如果產品需求沒有特別復雜的動畫要求,可以使用DefaultItemAnimator實現簡單的動畫效果。DefaultItemAnimator動畫的實現流程和原理已經在上兩節中做過簡單介紹,如果還沒有看過的童鞋,最好先打眼掃一下之前兩節的內容,有助於理解。附上鏈接地址:

1RecyclerView.ItemAnimator終極解讀(一)--RecyclerView源碼解析

2RecyclerView.ItemAnimator終極解讀(二)--SimpleItemAnimator和DefaultItemAnimator源碼解析


這一節,我們主要通過繼承DefaultItemAnimator來實現稍微復雜一點的自定義動畫。效果如下兩張圖所示:

\ \

如上兩張圖所示:每點擊一個item時,更新所點擊item的背景顏色和文本信息。 第一張圖是沒有添加動畫效果, 第二張圖是添加自定義動畫之後的效果。



主要代碼就是在自定義的動畫類MyDefaultItemAnimator.java中, 如下所示:
package material.danny_jiang.com.mydefaultitemanimator;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.support.annotation.NonNull;
import android.support.v4.util.ArrayMap;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.List;

/**
 * Created by axing on 16/5/25.
 */
public class MyDefaultItemAnimator extends DefaultItemAnimator {
    private static final String TAG = "MyDefaultItemAnimator";

    // 定義動畫執行時的加速度
    private AccelerateInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
    private DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator();

    ArgbEvaluator mColorEvaluator = new ArgbEvaluator();

    /**
     * 定義正在執行Animator的ViewHolder的Map集合
     * 此集合會保存用戶點擊的ViewHolder對象,目的在於當用戶不停的點擊某一item時
     * 會先判斷此ViewHolder種的itemView動畫是否正在執行,如果正在執行則停止
     */
    private ArrayMap mAnimatorMap = new ArrayMap<>();

    /**
     * 復寫canReuseUpdatedViewHolder方法並返回true,通知RecyclerView在執行動畫時可以復用ViewHolder對象
     * @param viewHolder
     * @return
     */
    @Override
    public boolean canReuseUpdatedViewHolder(RecyclerView.ViewHolder viewHolder) {
        return true;
    }

    /**
     * 自定義getItemHolderInfo方法,將ViewHolder中的背景顏色和TextView的文本信息傳入ColorTextInfo中
     * @param viewHolder
     * @param info
     * @return
     */
    @NonNull
    private ItemHolderInfo getItemHolderInfo(MyViewHolder viewHolder, ColorTextInfo info) {
        //獲取當前正在操作的ViewHolder對象
        final MyViewHolder myHolder = viewHolder;
        //獲取ViewHolder中itemView背景顏色
        final int bgColor = ((ColorDrawable) myHolder.container.getBackground()).getColor();
        //將背景顏色和TextView的文本信息賦值給ColorTextInfo對象的color和text變量
        info.color = bgColor;
        info.text = (String) myHolder.textView.getText();
        return info;
    }

    /**
     * 通過ViewHolder對象獲取動畫執行之前itemView中的背景顏色和文本信息
     * 初始化ColorTextInfo對象,並將背景顏色和文本信息進行賦值
     * @return
     */
    @NonNull
    @Override
    public ItemHolderInfo recordPreLayoutInformation(RecyclerView.State state,
                                                     RecyclerView.ViewHolder viewHolder, int changeFlags, List payloads) {
        Log.e(TAG, "recordPreLayoutInformation: " + viewHolder.toString());
        ColorTextInfo info = (ColorTextInfo) super.recordPreLayoutInformation(state, viewHolder,
                changeFlags, payloads);
        return getItemHolderInfo((MyViewHolder) viewHolder, info);
    }

    /**
     * 通過ViewHolder對象獲取動畫執行之後itemView中的背景顏色和文本信息
     * 初始化ColorTextInfo對象,並將背景顏色和文本信息進行賦值
     * @return
     */
    @NonNull
    @Override
    public ItemHolderInfo recordPostLayoutInformation(@NonNull RecyclerView.State state,
                                                      @NonNull RecyclerView.ViewHolder viewHolder) {
        Log.e(TAG, "recordPostLayoutInformation: " + viewHolder.toString());
        ColorTextInfo info = (ColorTextInfo) super.recordPostLayoutInformation(state, viewHolder);
        return getItemHolderInfo((MyViewHolder) viewHolder, info);
    }

    /**
     * 復寫obtainHolderInfo,返回自定義的ItemHolderInfo對象
     * @return
     */
    @Override
    public ItemHolderInfo obtainHolderInfo() {
        Log.e(TAG, "obtainHolderInfo: ");
        return new ColorTextInfo();
    }

    /**
     * 自定義ItemHolderInfo對象,持有兩個變量,依次來表示每一個Item的背景顏色和文本信息
     */
    private class ColorTextInfo extends ItemHolderInfo {
        int color;
        String text;
    }

    /**
     * 創建執行animateChange的動畫Info對象,內部封裝了所需要執行一個動畫類的相關信息
     * 起始alpha屬性動畫,和起始旋轉屬性動畫
     */
    private class AnimatorInfo {
        Animator overallAnim;
        ObjectAnimator fadeToBlackAnim, fadeFromBlackAnim, oldTextRotator, newTextRotator;

        public AnimatorInfo(Animator overallAnim,
                            ObjectAnimator fadeToBlackAnim, ObjectAnimator fadeFromBlackAnim,
                            ObjectAnimator oldTextRotator, ObjectAnimator newTextRotator) {
            this.overallAnim = overallAnim;
            this.fadeToBlackAnim = fadeToBlackAnim;
            this.fadeFromBlackAnim = fadeFromBlackAnim;
            this.oldTextRotator = oldTextRotator;
            this.newTextRotator = newTextRotator;
        }
    }


    /**
     * Custom change animation. Fade to black on the container background, then back
     * up to the new bg coolor. Meanwhile, the text rotates, switching along the way.
     * If a new change animation occurs on an item that is currently animating
     * a change, we stop the previous change and start the new one where the old
     * one left off.
     * 真正的執行change動畫的方法:
     * 通過傳入的preInfo和postInfo,分別將動畫前後的背景色和文本信息設置到alpha屬性動畫和旋轉屬性動畫中
     */
    @Override
    public boolean animateChange(@NonNull final RecyclerView.ViewHolder oldHolder,
                                 @NonNull final RecyclerView.ViewHolder newHolder,
                                 @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
        Log.e(TAG, "animateChange: ");

        if (oldHolder != newHolder) {
            //第一次顯示所有的RecyclerView時,新舊ViewHolder是不相等的
            return super.animateChange(oldHolder, newHolder, preInfo, postInfo);
        }

        final MyViewHolder viewHolder = (MyViewHolder) newHolder;

        // 獲取動畫前後的背景色和文本信息
        ColorTextInfo oldInfo = (ColorTextInfo) preInfo;
        ColorTextInfo newInfo = (ColorTextInfo) postInfo;
        int oldColor = oldInfo.color;
        int newColor = newInfo.color;
        final String oldText = oldInfo.text;
        final String newText = newInfo.text;

        // 獲取需要被執行動畫的View視圖對象
        LinearLayout newContainer = viewHolder.container;
        final TextView newTextView = viewHolder.textView;

        // 從mAnimatorMap緩存中查找當前newHolder對應的itemView動畫是否在執行中,如果是則終止動畫
        AnimatorInfo runningInfo = mAnimatorMap.get(newHolder);
        long prevAnimPlayTime = 0;
        boolean firstHalf = false;
        if (runningInfo != null) {
            firstHalf = runningInfo.oldTextRotator != null &&
                    runningInfo.oldTextRotator.isRunning();
            prevAnimPlayTime = firstHalf ?
                    runningInfo.oldTextRotator.getCurrentPlayTime() :
                    runningInfo.newTextRotator.getCurrentPlayTime();
            runningInfo.overallAnim.cancel();
        }

        // 初始化背景顏色漸變的屬性動畫
        ObjectAnimator fadeToBlack = null, fadeFromBlack;
        if (runningInfo == null || firstHalf) {
            int startColor = oldColor;
            if (runningInfo != null) {
                startColor = (Integer) runningInfo.fadeToBlackAnim.getAnimatedValue();
            }
            fadeToBlack = ObjectAnimator.ofInt(newContainer, "backgroundColor",
                    startColor, Color.BLACK);
            fadeToBlack.setEvaluator(mColorEvaluator);
            if (runningInfo != null) {
                fadeToBlack.setCurrentPlayTime(prevAnimPlayTime);
            }
        }

        fadeFromBlack = ObjectAnimator.ofInt(newContainer, "backgroundColor",
                Color.BLACK, newColor);
        fadeFromBlack.setEvaluator(mColorEvaluator);
        if (runningInfo != null && !firstHalf) {
            fadeFromBlack.setCurrentPlayTime(prevAnimPlayTime);
        }

        AnimatorSet bgAnim = new AnimatorSet();
        if (fadeToBlack != null) {
            bgAnim.playSequentially(fadeToBlack, fadeFromBlack);
        } else {
            bgAnim.play(fadeFromBlack);
        }

        // 初始化旋轉的屬性動畫
        ObjectAnimator oldTextRotate = null, newTextRotate;
        if (runningInfo == null || firstHalf) {
            oldTextRotate = ObjectAnimator.ofFloat(newTextView, View.ROTATION_X, 0, 90);
            oldTextRotate.setInterpolator(mAccelerateInterpolator);
            if (runningInfo != null) {
                oldTextRotate.setCurrentPlayTime(prevAnimPlayTime);
            }
            oldTextRotate.addListener(new AnimatorListenerAdapter() {
                boolean mCanceled = false;
                @Override
                public void onAnimationStart(Animator animation) {
                    newTextView.setText(oldText);
                }

                @Override
                public void onAnimationCancel(Animator animation) {
                    mCanceled = true;
                }

                @Override
                public void onAnimationEnd(Animator animation) {
                    if (!mCanceled) {
                        //old動畫執行之後,需要重新設置文本信息
                        newTextView.setText(newText);
                    }
                }
            });
        }

        newTextRotate = ObjectAnimator.ofFloat(newTextView, View.ROTATION_X, -90, 0);
        newTextRotate.setInterpolator(mDecelerateInterpolator);
        if (runningInfo != null && !firstHalf) {
            newTextRotate.setCurrentPlayTime(prevAnimPlayTime);
        }

        AnimatorSet textAnim = new AnimatorSet();
        if (oldTextRotate != null) {
            textAnim.playSequentially(oldTextRotate, newTextRotate);
        } else {
            textAnim.play(newTextRotate);
        }

        AnimatorSet changeAnim = new AnimatorSet();
        changeAnim.playTogether(bgAnim, textAnim);
        changeAnim.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                dispatchAnimationFinished(newHolder);
                mAnimatorMap.remove(newHolder);
            }
        });
        changeAnim.start();

        AnimatorInfo runningAnimInfo = new AnimatorInfo(changeAnim, fadeToBlack, fadeFromBlack,
                oldTextRotate, newTextRotate);
        mAnimatorMap.put(newHolder, runningAnimInfo);

        return true;
    }

    @Override
    public void endAnimation(RecyclerView.ViewHolder item) {
        super.endAnimation(item);
        if (!mAnimatorMap.isEmpty()) {
            final int numRunning = mAnimatorMap.size();
            for (int i = numRunning; i >= 0; i--) {
                if (item == mAnimatorMap.keyAt(i)) {
                    mAnimatorMap.valueAt(i).overallAnim.cancel();
                }
            }
        }
    }

    @Override
    public boolean isRunning() {
        return super.isRunning() || !mAnimatorMap.isEmpty();
    }

    @Override
    public void endAnimations() {
        super.endAnimations();
        if (!mAnimatorMap.isEmpty()) {
            final int numRunning = mAnimatorMap.size();
            for (int i = numRunning; i >= 0; i--) {
                mAnimatorMap.valueAt(i).overallAnim.cancel();
            }
        }
    }
}

每段代碼的含義的用途都已經在代碼中添加了相應的注釋,請耐得住寂寞,仔細查看^_^ 自定義ItemAnimator實現好之後,剩下的就是初始化RecyclerView,並通過setItemAnimator方法將自定義ItemAnimator對象傳遞給RecyclerView。
MainActivity.java中的相關代碼如下:
recyclerView = ((RecyclerView) findViewById(R.id.recyclerview));
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(new RecyclerAdapter(this, recyclerView));
recyclerView.setItemAnimator(new MyDefaultItemAnimator());


RecyclerAdapter.java代碼如下:
@Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        final MyViewHolder myHolder = (MyViewHolder) holder;
        int color = mColors.get(position);
        myHolder.container.setBackgroundColor(color);
        myHolder.textView.setText("#" + Integer.toHexString(color));
    }

    @Override
    public int getItemCount() {
        return mColors.size();
    }
private View.OnClickListener mItemAction = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            changeItem(v);
        }
    };

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View container = mainActivity.getLayoutInflater().inflate(R.layout.item_layout, parent, false);
        container.setOnClickListener(mItemAction);
        return new MyViewHolder(container);
    }

    private int generateColor() {
        int red = ((int) (Math.random() * 200));
        int green = ((int) (Math.random() * 200));
        int blue = ((int) (Math.random() * 200));
        return Color.rgb(red, green, blue);
    }
private void generateData() {
        for (int i = 0; i < 100; ++i) {
            mColors.add(generateColor());
        }
    }
private void changeItem(View view) {
        int position = mRecyclerView.getChildAdapterPosition(view);
        if (position != RecyclerView.NO_POSITION) {
            int color = generateColor();
            mColors.set(position, color);
            notifyItemChanged(position);
        }
    }


MyViewHolder.java代碼如下:
package material.danny_jiang.com.mydefaultitemanimator;

import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

/**
 * Created by axing on 16/5/25.
 */
class MyViewHolder extends RecyclerView.ViewHolder {
    public TextView textView;
    public LinearLayout container;

    public MyViewHolder(View v) {
        super(v);
        container = (LinearLayout) v;
        textView = (TextView) v.findViewById(R.id.textview);
    }

    @Override
    public String toString() {
        return super.toString() + " \"" + textView.getText() + "\"";
    }
}


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved