RecyclerView(6)-自定义ItemAnimator

作者: wenld_ | 来源:发表于2017-06-28 11:28 被阅读756次

    自定义ItemAnimtor不算太难,更何况还有一个官方的默认动画类DefaultItemAnimator可以参考,相信很多同学都是会的,会自定义ItemAnimtor的同学可以略过本篇,今天不扯淡了 直接进入主题!

    目录如下:
    1、 一些基础概念
    2、 源码简单分析
    · 2.1、以notifyItemInserted为入口梳理RecyclerView中Animation流程
    · 2.2、 DefaultItemAnimtor解读
    3、 自定义RecyclerView.ItemAnimator
    · 3.1 先生骨架
    · 3.2 再长血肉

    1、一些基础概念

    官方有一个默认Item动画类DafaultItemAnimator, 其中 DefaultItemAnimator继承了SimpleItemAnimator , 在继承了RecyclerView.ItemAnimator
    1、SimpleItemAnimator 它是一个包装类,用来判断当前的ViewHolder到底是执行移动、移除、添加或者改变等行为。

    2、DefaultItemAnimator 是执行具体动画类,它负责将viewHolder初始化、保存需要执行动画的ViveHolder以及动画信息、执行具体的动画。 我们自定义ItemAnimtor 也是仿照这个来的。

    3、 其中DefaultItemAnimatoranimate + 动作 为名的的方法(比如animateAdd())做的事情有:计算动画信息,保存动画信息,初始化view的状态,且可以控制该VIewHolder是否执行该次动画,如果返回值为false那么那么不会执行该ViewHolder的改次动画;

    4、 DefaultItemAnimatoranimate + 动作 + Impl 为名的方法,做的动作是执行具体的动画动作。

    5、runPendingAnimations是最终执行具体动画的方法

    2、 源码简单分析

    2.1 以notifyItemInserted为入口梳理RecyclerView中Animation流程

    notifyitemInserted()为入口分析 触发一个itemView的动画都经历了哪些类哪些方法;ps:其它 notifyitemitem...等方法流程与之类似

    1、触发notifyitemInserted方法;
    2、调用mObservable.notifyItemRangeInserted被观察者方法;
    3、遍历所注册的观察者RecyclerViewDataObserver 并调用其 onItemRangeInserted()方法;
    4 & 5、添加动画标识UpdateOp并保存在队列中
    7 & 8、执行 mUpdateChildViewsRunnable : RunnableRun方法,调用 consumePendingUpdateOperations方法
    10&11&12&13&14&15&16 调用 这里做了一些列动画基础数据的配置与存储
    17、调用 dispatchLayout方法,先会调用layoutManager的布局view 添加view;后在调用 dispatchLayoutStep3方法
    18、dispatchLayoutStep3()该方法是这样解释的

     * The final step of the layout where we save the information about views for animations,
         * trigger animations and do any necessary cleanup.
    

    最终在布局完成后,会触发item的动画
    19-end 、调用ViewInfoStore.ProcessCallback.processPersistent方法,其中内部调用到了 ItemAnimtoranimateChange 方法 ,在去调用 ItemAnimtor . runPendingAnimations执行实现类设置的动画。

    一个简单的 触发Item动画 时序过程就是如此了。 可以看到我们最终执行动画都是在 RecyclerView中的dispatchLayoutStep3执行的。执行具体动画在 ItemAnimtor类的runPendingAnimations方法

    2.2 DefaultItemAnimtor解读

    这边着重得来分析一下 DefaultItemAnimtorrunPendingAnimations方法,因为该方法最终执行动画

       @Override
       public void runPendingAnimations() {
     //  判断是否 remove、move、change、add 列表是否为空
           boolean removalsPending = !mPendingRemovals.isEmpty();
           boolean movesPending = !mPendingMoves.isEmpty();
           boolean changesPending = !mPendingChanges.isEmpty();
           boolean additionsPending = !mPendingAdditions.isEmpty();
           if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
               return;
           }
           // First, remove stuff  先执行 remove列表的动画
           for (ViewHolder holder : mPendingRemovals) {
               animateRemoveImpl(holder);
           }
    // 清空存储
           mPendingRemovals.clear();
           // Next, move stuff  执行move动画
           if (movesPending) {
               final ArrayList<MoveInfo> moves = new ArrayList<>();
               moves.addAll(mPendingMoves);
               mMovesList.add(moves);
               mPendingMoves.clear();
               Runnable mover = new Runnable() {
                   @Override
                   public void run() {
                       for (MoveInfo moveInfo : moves) {
                           animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
                                   moveInfo.toX, moveInfo.toY);
                       }
                       moves.clear();
                       mMovesList.remove(moves);
                   }
               };
               if (removalsPending) {
                   View view = moves.get(0).holder.itemView;
                   ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
               } else {
                   mover.run();
               }
           }
           // Next, change stuff, to run in parallel with move animations   执行vhange动画
           if (changesPending) {
               final ArrayList<ChangeInfo> changes = new ArrayList<>();
               changes.addAll(mPendingChanges);
               mChangesList.add(changes);
               mPendingChanges.clear();
               Runnable changer = new Runnable() {
                   @Override
                   public void run() {
                       for (ChangeInfo change : changes) {
                           animateChangeImpl(change);
                       }
                       changes.clear();
                       mChangesList.remove(changes);
                   }
               };
               if (removalsPending) {
                   ViewHolder holder = changes.get(0).oldHolder;
                   ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
               } else {
                   changer.run();
               }
           }
           // Next, add stuff   执行添加动画
           if (additionsPending) {
               final ArrayList<ViewHolder> additions = new ArrayList<>();
               additions.addAll(mPendingAdditions);
               mAdditionsList.add(additions);
               mPendingAdditions.clear();
               Runnable adder = new Runnable() {
                   @Override
                   public void run() {
                       for (ViewHolder holder : additions) {
                           animateAddImpl(holder);
                       }
                       additions.clear();
                       mAdditionsList.remove(additions);
                   }
               };
               if (removalsPending || movesPending || changesPending) {
                   long removeDuration = removalsPending ? getRemoveDuration() : 0;
                   long moveDuration = movesPending ? getMoveDuration() : 0;
                   long changeDuration = changesPending ? getChangeDuration() : 0;
                   long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
                   View view = additions.get(0).itemView;
                   ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
               } else {
                   adder.run();
               }
           }
       }
    
    

    通过以上信息可以了解到:runPendingAnimations最终执行动画操作,
    其中animate + 动作 + Impl 为名的方法,做具体的动画实现。

    3、 自定义RecyclerView.ItemAnimator

    3.1 先生骨架

    我们要自定义一个BaseItemAnimator ,类图如下所示

    其中BaseItemAnimator仿照DefaultItemAnimator 的一个结构,唯一不同的是 执行具体动画代码被抽象为了方法。

    FadeItemAnimatorRotateItemAnimator 等继承BaseItemAnimator 是具体动画的实现类
    这边仿照 DefaultItemAnimator 搭建据图框架Base框架

    public class BaseItemAnimator extends SimpleItemAnimator {
        //业务控制是否执行该viewHolder的动画  比如通讯录列表,判断只有联
        //系人的ViewHolder执行动画,如果是分组头部ViewHolder则不执行动画
        @Override
        public boolean animateRemove(RecyclerView.ViewHolder holder) {
            // 计算 holder的动画参数 如:x偏移量 y轴偏移量 透明度等等
            // 保存 viewHolder 以及 动画参数
            //  业务控制是否执行该viewHolder的动画  
            //      比如通讯录列表,判断只有联系人的ViewHolder执行动画,
            //      如果是分组头部ViewHolder则不执行动画
            return false;
        }
    
        @Override
        public boolean animateAdd(RecyclerView.ViewHolder holder) {
            // 计算 holder的动画参数 如:x偏移量 y轴偏移量 透明度等等
            // 保存 viewHolder 以及 动画参数
            //  业务控制是否执行该viewHolder的动画  
            return false;
        }
    
    
        //用于控制添加,移动更新时,其它Item的动画执行
        @Override
        public boolean animateMove(RecyclerView.ViewHolder holder, int fromX, int fromY, int toX, int toY) {
            // 计算 holder的动画参数 如:x偏移量 y轴偏移量 透明度等等
            // 保存 viewHolder 以及 动画参数
            //  业务控制是否执行该viewHolder的动画  
            return false;
        }
    
        //Item更新回调
        @Override
        public boolean animateChange(RecyclerView.ViewHolder oldHolder, RecyclerView.ViewHolder newHolder, int fromLeft, int fromTop, int toLeft, int toTop) {
            // 计算 holder的动画参数 如:x偏移量 y轴偏移量 透明度等等
            // 保存 viewHolder 以及 动画参数
            //  业务控制是否执行该viewHolder的动画  
            return false;
        }
    
        //真正控制执行动画的地方
        @Override
        public void runPendingAnimations() {
                // 根据保存的 viewHolder 以及 animInfo   执行动画
        }
    
        //停止某个Item的动画
        @Override
        public void endAnimation(RecyclerView.ViewHolder item) {
              
        }
    
        //停止所有动画
        @Override
        public void endAnimations() {
    
        }
    
        @Override
        public boolean isRunning() {
            return false;
        }
    
    
      /**
         * 执行移除动画
         * @param holder 被移除的ViewHolder
         * @param animator 被移动的ViewHolder对应动画对象
         */
        public abstract void setRemoveAnimation(ViewHolder holder,ViewPropertyAnimatorCompat animator);
        /**
         * 执行移除动画结束,执行还原,因为该ViewHolder会被复用
         * @param holder 被移除的ViewHolder
         */
        public abstract void removeAnimationEnd(ViewHolder holder);
        /**
         * 执行添加动画初始化 这里设置透明为0添加时就会有渐变效果当然你可以在执行动画代码之前执行
         * @param holder 添加的ViewHolder
         */
        public abstract void addAnimationInit(ViewHolder holder);
        /**
         * 执行添加动画
         * @param holder 添加的ViewHolder
         * @param animator 添加的ViewHolder对应动画对象
         */
        public abstract void setAddAnimation(ViewHolder holder,ViewPropertyAnimatorCompat animator);
        /**
         * 取消添加还原状态以复用
         * @param holder 添加的ViewHolder
         */
        public abstract void addAnimationCancel(ViewHolder holder);
        /**
         * 更新时旧的ViewHolder动画
         * @param holder 旧的ViewHolder
         * @param animator ViewHolder对应动画对象
         */
        public abstract void setOldChangeAnimation(ViewHolder holder,ViewPropertyAnimatorCompat animator);
        /**
         * 更新时旧的ViewHolder动画结束,执行还原
         * @param holder
         */
        public abstract void oldChangeAnimationEnd(ViewHolder holder);
        /**
         * 更新时新的ViewHolder初始化
         * @param holder 更新时新的ViewHolder
         */
        public abstract void newChangeAnimationInit(ViewHolder holder);
        /**
         * 更新时新的ViewHolder动画
         * @param holder 新的ViewHolder
         * @param animator ViewHolder对应动画对象
         */
        public abstract void setNewChangeAnimation(ViewHolder holder,ViewPropertyAnimatorCompat animator);
        /**
         * 更新时新的ViewHolder动画结束,执行还原
         * @param holder
         */
        public abstract void newChangeAnimationEnd(ViewHolder holder);
    }
    

    3.2、再长血肉

    具体实现我这边只写一个 FadeItemAnimator

    public class FadeItemAnimator extends BaseItemAnimator {
        /**
         * 执行移除动画
         * @param holder 被移除的ViewHolder
         * @param animator 被移动的ViewHolder对应动画对象
         */
        @Override
        public void setRemoveAnimation(RecyclerView.ViewHolder holder, ViewPropertyAnimatorCompat animator) {
            animator.alpha(0);
        }
    
        /**
         * 执行移除动画结束,执行还原,因为该ViewHolder会被复用
         * @param view 被移除的ViewHolder
         */
        @Override
        public void removeAnimationEnd(RecyclerView.ViewHolder view) {
            ViewCompat.setAlpha(view.itemView,1);
        }
    
        /**
         * 执行添加动画初始化 这里设置透明为0添加时就会有渐变效果当然你可以在执行动画代码之前执行
         * @param holder 添加的ViewHolder
         */
        @Override
        public void addAnimationInit(RecyclerView.ViewHolder holder) {
            ViewCompat.setAlpha(holder.itemView, 0);
        }
    
        /**
         * 执行添加动画
         * @param holder 添加的ViewHolder
         * @param animator 添加的ViewHolder对应动画对象
         */
        @Override
        public void setAddAnimation(RecyclerView.ViewHolder holder,ViewPropertyAnimatorCompat animator) {
            animator.alpha(1);
        }
    
        /**
         * 取消添加还原状态以复用
         * @param holder 添加的ViewHolder
         */
        @Override
        public void addAnimationCancel(RecyclerView.ViewHolder holder) {
            ViewCompat.setAlpha(holder.itemView, 1);
        }
    
        /**
         * 更新时旧的ViewHolder动画
         * @param holder 旧的ViewHolder
         * @param animator ViewHolder对应动画对象
         */
        @Override
        public void setOldChangeAnimation(RecyclerView.ViewHolder holder, ViewPropertyAnimatorCompat animator) {
            animator.alpha(0);
        }
    
        /**
         * 更新时旧的ViewHolder动画结束,执行还原
         * @param holder
         */
        @Override
        public void oldChangeAnimationEnd(RecyclerView.ViewHolder holder) {
            ViewCompat.setAlpha(holder.itemView,1);
        }
    
        /**
         * 更新时新的ViewHolder初始化
         * @param holder 更新时新的ViewHolder
         */
        @Override
        public void newChangeAnimationInit(RecyclerView.ViewHolder holder) {
            ViewCompat.setAlpha(holder.itemView,0);
        }
    
        /**
         * 更新时新的ViewHolder动画
         * @param holder 新的ViewHolder
         * @param animator ViewHolder对应动画对象
         */
        @Override
        public void setNewChangeAnimation(RecyclerView.ViewHolder holder, ViewPropertyAnimatorCompat animator) {
            animator.alpha(1);
        }
    
        /**
         * 更新时新的ViewHolder动画结束,执行还原
         * @param holder
         */
        @Override
        public void newChangeAnimationEnd(RecyclerView.ViewHolder holder) {
            ViewCompat.setAlpha(holder.itemView,1);
        }
    }
    

    其他动画实现具体查看源码,也可以参考DefaultItemAnimtor 实现动画细节,当然在下面有对DefaultItemAnimtor 做一个源码分析。

    源码戳我!

    · RecyclerView(1)- Decoration源码解析
    · RecyclerView(2)- 自定义Decoration打造时光轴效果
    · RecyclerView(3)- LayoutMagager源码解析,LinearLayoutManager
    · RecyclerView(4)- 核心、Recycler复用机制
    · RecyclerView(5)- 自定义LayoutManager(布局、复用)
    · RecyclerView(6)- 自定义ItemAnimator
    · RecyclerView(7)- ItemTouchHelper
    · RecyclerView(8)- MultiTypeAdapter文章MultiTypeAdapter Github地址


    希望我的文章不会误导在观看的你,如果有异议的地方欢迎讨论和指正。
    如果能给观看的你带来收获,那就是最好不过了。

    人生得意须尽欢, 桃花坞里桃花庵
    点个关注呗,对,不信你点试试?

    相关文章

      网友评论

        本文标题:RecyclerView(6)-自定义ItemAnimator

        本文链接:https://www.haomeiwen.com/subject/kyedcxtx.html