RecyclerView 源码分析(四)RecyclerView的动画机制

转载自琼珶和予

RecyclerView 源码分析(四)RecyclerView的动画机制

  • RecyclerView的动画机制
    • 1. 概述
      • 1. 再来看RecyclerView的三大流程
      • 2. 从Adapter角度来看动画执行的机制
        • (1) 通过观察者模式来实现RecyclerView 和Adapter的通信
        • (2) 为什么notifyDataSetChanged方法不会执行动画呢?
    • 3. 总结

RecyclerView的动画机制

本文不会分析ItemAnimator相关的知识,而是理解RecyclerView怎么执行ItemAnimator的,有关ItemAniamtor的知识,后面我会写专门的文章来分析。

本文参考资料:

RecyclerView animations - AndroidDevSummit write-up
RecyclerView.ItemAnimator终极解读(一)–RecyclerView源码解析
  
注意,本文所有的代码都来自于27.1.1。

1. 概述

RecyclerView之所以受欢迎,有一部分的原因得归功于它的动画机制。我们可以通过RecyclerView的setItemAnimator方法来给每个Item设置在不同行为下,执行不同的动画,非常的简单。尽管我们知道怎么给RecyclerView设置动画,但是RecyclerView是怎么通过ItemAnimator来给每个Item实现动画,这里面的原理值得我们去研究和学习。

在正式分析RecyclerView的动画机制之前,我们先对几个词语有一个概念,我们来看看:

词语 含义
Disappearance 表示在动画之前,ItemView是可见的,动画之后就可不见了。这里的操作包括,remove操作和普通的滑动导致ItemView划出屏幕
Appearance 表示动画之前,ItemView是不可见,动画之后就可见了。这里的操作包括,add操作和普通的滑动导致ItemView划入屏幕
Persistence 表示动画前后,状态是不变的。这里面的操作包括,无任何操作
change 表示动画前后,状态是不变的。这里面的操作包括,change操作

还有注意的一点就是,ViewHolder不是用来记录ItemView的位置信息,而是进行数据绑定的,所以在动画中,关于位置信息的记录不是依靠ViewHolder来实现的,而是依靠一个叫ItemHolderInfo的类实现的,在这个类里面,有四个成员变量,分别记录ItemView的left、top、right和bottom四个位置信息

最后还需要注意一点就是,我们从RecyclerView的三大流程中可以得到,在RecyclerView的内部,dispatchLayout分为三步,其中dispathchLayoutStep1被称为预布局,在这里主要是保存ItemView的OldViewHolder,同时还会记录下每个ItemView在动画之前的位置信息;与之对应的dispathchLayoutStep3被称为后布局,主要结合真正布局和预布局的相关信息来实现进行动画,当然前提是RecyclerView本身支持动画。

本文打算从两个角度来分析RecyclerView的动画,一是从普通三大的流程来看,这是动画机制的核心所在;二是从Adapeter的角度上来看,看看我们每次在调用Adapter的notify相关方法之后,是怎么进行执行动画的(实际上也是回到三大流程里面)。

1. 再来看RecyclerView的三大流程

取这个题目,我感觉有特别的含义。首先,本次分析动画机制就是重新来看看三大流程,当然本次分三大流程肯定没有之前的那么仔细,其次侧重点也不同;其次,本次再来看RecyclerView的三大流程,还可以填之前在分析RecyclerView的三大流程留下的坑。
  
本次的分析重点在于dispathchLayoutStep1和dispathchLayoutStep3,不会分析完整的三大流程,所以,还有不懂RecyclerView三大流程的同学,可以参考我的文章:RecyclerView 源码分析(一) - RecyclerView的三大流程。

我们先来看看dispatchLayoutStep1方法:

private void dispatchLayoutStep1() {
   
    // ······
    if (mState.mRunSimpleAnimations) {
   
        // Step 0: Find out where all non-removed items are, pre-layout
        int count = mChildHelper.getChildCount();
        for (int i = 0; i < count; ++i) {
   
            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
            if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {
   
                continue;
            }
            final ItemHolderInfo animationInfo = mItemAnimator
                    .recordPreLayoutInformation(mState, holder,
                            ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
                            holder.getUnmodifiedPayloads());
            mViewInfoStore.addToPreLayout(holder, animationInfo);
            if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
                    && !holder.shouldIgnore() && !holder.isInvalid()) {
   
                long key = getChangedHolderKey(holder);
                // This is NOT the only place where a ViewHolder is added to old change holders
                // list. There is another case where:
                //    * A VH is currently hidden but not deleted
                //    * The hidden item is changed in the adapter
                //    * Layout manager decides to layout the item in the pre-Layout pass (step1)
                // When this case is detected, RV will un-hide that view and add to the old
                // change holders list.
                mViewInfoStore.addToOldChangeHolders(key, holder);
            }
        }
    }
    if (mState.mRunPredictiveAnimations) {
   
        // Step 1: run prelayout: This will use the old positions of items. The layout manager
        // is expected to layout everything, even removed items (though not to add removed
        // items back to the container). This gives the pre-layout position of APPEARING views
        // which come into existence as part of the real layout.

        // Save old positions so that LayoutManager can run its mapping logic.
        saveOldPositions();
        final boolean didStructureChange = mState.mStructureChanged;
        mState.mStructureChanged = false;
        // temporarily disable flag because we are asking for previous layout
        mLayout.onLayoutChildren(mRecycler, mState);
        mState.mStructureChanged = didStructureChange;

        for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
   
            final View child = mChildHelper.getChildAt(i);
            final ViewHolder viewHolder = getChildViewHolderInt(child);
            if (viewHolder.shouldIgnore()) {
   
                continue;
            }
            if 

你可能感兴趣的:(RecyclerView 源码分析(四)RecyclerView的动画机制)