RecyclerView源码分析(四)--动画流程

看完本文你大概需要 8.3分 的毅力

相关系列文章

RecyclerView源码分析(一)--整体设计
RecyclerView源码分析(二)--测量流程
RecyclerView源码分析(三)--布局流程

上一篇文章讲了RecyclerView的布局流程,发现里面大多数内容都是和动画相关的。那么这边文章就先讲RecyclerView中,数据改变发出通知到播放动画的一系列流程。

RecyclerView的动画流程

对于RecyclerView的动画流程,是一个非常的长的流程,那么我们先把大的东西分部分来看,会轻松一点。首先,回想一下,我们通常在RecyclerView中数据改变的时候,调用什么函数来使其播放动画的。通常我们都是调用Adapter的以下函数。

notifyDataSetChanged()

notifyItemChanged(int position)

notifyItemRangeChanged(int positionStart, int itemCount)

notifyItemRangeChanged(int positionStart, int itemCount, Object payload)

notifyItemInserted(int position)

notifyItemMoved(int fromPosition, int toPosition)

notifyItemRangeInserted(int positionStart, int itemCount)

notifyItemRemoved(int position)

notifyItemRangeRemoved(int positionStart, int itemCount)

下面我们把这些函数统称为Adapter的通知函数

那么我们就从这里出发开始RecyclerView的动画流程。而在上一篇布局流程源码分析中,在最后总结时提到的。

RecyclerView的数据改变的动画是在布局过程的第三步中统一触发的。并不是一调用notify之后立即触发。

可以看出动画的一部分的处理是在布局过程中完成的,那么我们可以把动画流程分为两个部分。布局过程到动画触发的部分,和Adapter发出通知到布局之前的部分。

下面我们以插入作为例子分析,其他过程大致类似。

Adapter通知到布局之前的处理

首先,盗用该系列文章中的第一篇文章介绍Adapter通知流程的图:

RecyclerView源码分析(四)--动画流程_第1张图片

Adapter的通知过程是一个观察者模式。结合源码:

public static abstract class Adapter {

    private final AdapterDataObservable mObservable = new AdapterDataObservable();
    
    public void registerAdapterDataObserver(AdapterDataObserver observer) {
        mObservable.registerObserver(observer);
    }
    
    public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
        mObservable.unregisterObserver(observer);
    }
    
    public final void notifyItemInserted(int position) {
        mObservable.notifyItemRangeInserted(position, 1);
    }
}

我们可以看到Adapter中包含一个AdapterDataObservable的对象mObservable,这个是一个可观察者,在可观察者中可以注册一系列的观察者AdapterDataObserver。在我们调用的notify函数的时候,就是可观察者发出通知,这时已经注册的观察者都可以收到这个通知,然后依次进行处理。

那么我们看一下注册观察者的地方。

注册观察者的地方就是在RecyclerView的这个函数中。这个是setAdapter方法最终调用的地方。它主要做了:

  1. 如果之前存在Adapter,先移除原来的,注销观察者,和从RecyclerView Detached。
  2. 然后根据参数,决定是否清除原来的ViewHolder
  3. 然后重置AdapterHelper,并更新Adapter,注册观察者。
private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
            boolean removeAndRecycleViews) {
    if (mAdapter != null) {
        mAdapter.unregisterAdapterDataObserver(mObserver);
        mAdapter.onDetachedFromRecyclerView(this);
    }
    if (!compatibleWithPrevious || removeAndRecycleViews) {
        if (mItemAnimator != null) {
            mItemAnimator.endAnimations();
        }
        if (mLayout != null) {
            mLayout.removeAndRecycleAllViews(mRecycler);
            mLayout.removeAndRecycleScrapInt(mRecycler);
        }
        mRecycler.clear();
    }
    mAdapterHelper.reset();
    final Adapter oldAdapter = mAdapter;
    mAdapter = adapter;
    if (adapter != null) {
        adapter.registerAdapterDataObserver(mObserver);
        adapter.onAttachedToRecyclerView(this);
    }
    if (mLayout != null) {
        mLayout.onAdapterChanged(oldAdapter, mAdapter);
    }
    mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
    mState.mStructureChanged = true;
    markKnownViewsInvalid();
}

从这里我们可以看出,mObserver这个成员变量就是注册的观察者,那么我们去看看这个成员变量的内容。

该成员变量是一个RecyclerViewDataObserver的实例,那么RecyclerViewDataObserver实现了AdapterDataObserver中的方法。其中onItemRangeInserted(int positionStart, int itemCount)就是观察者接受到有数据插入通知的方法。那么我们来分析这个方法。看注释。

private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();

private class RecyclerViewDataObserver extends AdapterDataObserver {
    
    ……
    mPostUpdatesOnAnimation = version >= 16;

    @Override
    public void onItemRangeInserted(int positionStart, int itemCount) {
        // (1) 断言不在布局或者滚动过程中,其实就是如果在布局或者滚动过程中,则不会执
        // 行后面的内容
        assertNotInLayoutOrScroll(null);
        // (2) 这里小心,不要小看if括号中的内容,这是关键。我们去看看这个方法的实现。
        // 见下面注释 (3),在 (3) 返回true之后执行triggerUpdateProcessor方法,
        // triggerUpdateProcessor方法分析请看注释(4)。
        if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
            triggerUpdateProcessor();
        }
    }
}

AdapterHelper中onItemRangeInserted函数即相关内容,请看注释(3)。 

class AdapterHelper implements OpReorderer.Callback {

    // 一个待处理更新操作的列表,该列表中存放所有等待处理的操作信息。
    final ArrayList mPendingUpdates = new ArrayList();

    // (3) 该方法将插入操作的信息存储到一个UpdateOp中,并添加到待处理更新操作列表中,
    // 如果操作列表中的值是1,就返回真表示需要处理操作,等于1的判断避免重复触发处理操作。
    // obtainUpdateOp内部是通过池来得到一个UpdateOp对象。那么下面回去看我们注释 (4)。
    boolean onItemRangeInserted(int positionStart, int itemCount) {
        if (itemCount < 1) {
            return false;
        }
        mPendingUpdates.add(obtainUpdateOp(UpdateOp.ADD, positionStart, itemCount, null));
        mExistingUpdateTypes |= UpdateOp.ADD;
        return mPendingUpdates.size() == 1;
    }
}
// (4) 触发更新处理操作,分为两种情况,在 版本大于16 且 已经Attach 并且 设置了大小固定 的情况下,进行mUpdateChildViewsRunnable中的操作。否则请求布局。
void triggerUpdateProcessor() {
    if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
        ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
    } else {
        mAdapterUpdateDuringMeasure = true;
        requestLayout();
    }
}
// (5)只有在已经完成了第一次布局、有一个固定的大小(mHasFixedSize)、已经Attach、不冻结布局的情况下才进行
    final Runnable mUpdateChildViewsRunnable = new Runnable() {
        @Override
        public void run() {
            if (!mFirstLayoutComplete || isLayoutRequested()) {
                // a layout request will happen, we should not do layout here.
                return;
            }
            if (!mIsAttached) {
                requestLayout();
                // if we are not attached yet, mark us as requiring layout and skip
                return;
            }
            if (mLayoutFrozen) {
                mLayoutWasDefered = true;
                return; //we'll process updates when ice age ends.
            }
            consumePendingUpdateOperations();
        }
    };

    void consumePendingUpdateOperations() {
        //第一次布局还未完成、数据集更改未结束,返回
        if (!mFirstLayoutComplete || mDataSetHasChangedAfterLayout) {
            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
            dispatchLayout();
            TraceCompat.endSection();
            return;
        }
        //不需要更新
        if (!mAdapterHelper.hasPendingUpdates()) {
            return;
        }

        // (6) 如果只有更新类型的操作(这里指内容的更新,不影响View位置的改变)的情况下,
        // 先进行预处理,然后在没有View更新的情况下消耗延迟的更新操作,否则调用
        // dispatchLayout方法对RecyclerView中的View重新布局。那么接下来分析
        // preProcess()方法。
        if (mAdapterHelper.hasAnyUpdateTypes(AdapterHelper.UpdateOp.UPDATE) && !mAdapterHelper
                .hasAnyUpdateTypes(AdapterHelper.UpdateOp.ADD | AdapterHelper.UpdateOp.REMOVE
                        | AdapterHelper.UpdateOp.MOVE)) {
            TraceCompat.beginSection(TRACE_HANDLE_ADAPTER_UPDATES_TAG);
            startInterceptRequestLayout();
            onEnterLayoutOrScroll();
            //预处理
            mAdapterHelper.preProcess();
            if (!mLayoutWasDefered) {
                //存在View更新,重新布局
                if (hasUpdatedView()) {
                    dispatchLayout();
                } else {
                    // 消费掉延迟的更新操作
                    mAdapterHelper.consumePostponedUpdates();
                }
            }
            stopInterceptRequestLayout(true);
            onExitLayoutOrScroll();
            TraceCompat.endSection();
        } else if (mAdapterHelper.hasPendingUpdates()) {
            // (7) 在既有更新操作又有添加或者删除或者移动中任意一个的情况下,调用
            // dispatchLayout方法对RecyclerView中的View重新布局
            TraceCompat.beginSection(TRACE_ON_DATA_SET_CHANGE_LAYOUT_TAG);
            dispatchLayout();
            TraceCompat.endSection();
        }
    }

AdapterHelper preProcess方法源码: 

--> AdapterHelper.java
// 8) 预处理做了以下几件事情,<1> 先将待处理操作重排。<2> 应用所有操作 <3> 清空待处理操作列表,
// 以ADD为例分析流程。
void preProcess() {
    //待处理操作重排
    mOpReorderer.reorderOps(mPendingUpdates);
    final int count = mPendingUpdates.size();
    //遍历 应用所有操作
    for (int i = 0; i < count; i++) {
        UpdateOp op = mPendingUpdates.get(i);
        switch (op.cmd) {
            case UpdateOp.ADD:
                applyAdd(op);
                break;
            case UpdateOp.REMOVE:
                applyRemove(op);
                break;
            case UpdateOp.UPDATE:
                applyUpdate(op);
                break;
            case UpdateOp.MOVE:
                applyMove(op);
                break;
        }
        if (mOnItemProcessedCallback != null) {
            mOnItemProcessedCallback.run();
        }
    }
    //清空待处理操作列表,
    mPendingUpdates.clear();
}

// 9) 直接看postponeAndUpdateViewHolders
private void applyAdd(UpdateOp op) {
    postponeAndUpdateViewHolders(op);
}

// 10) 先将操作添加到推迟的操作列表中。然后将操作的内容交给回调处理。
private void postponeAndUpdateViewHolders(UpdateOp op) {
    mPostponedList.add(op);
    switch (op.cmd) {
        case UpdateOp.ADD:
            mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
            break;
        case UpdateOp.MOVE:
            mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
            break;
        case UpdateOp.REMOVE:
            mCallback.offsetPositionsForRemovingLaidOutOrNewView(op.positionStart,
                    op.itemCount);
            break;
        case UpdateOp.UPDATE:
            mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount, op.payload);
            break;
        default:
            throw new IllegalArgumentException("Unknown update op type for " + op);
    }
}

 该回调是在RecyclerView初始化的时候,初始化AdapterHelper的时候设置进来的。我们先只研究offsetPositionsForAdd这个回调分析流程。

 


    public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        ...
        initAdapterManager(); 
        initChildrenHelper();
        ....  
}


    void initAdapterManager() {
        mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
            ......

            @Override
            public void offsetPositionsForAdd(int positionStart, int itemCount) {
                //(11) 直接看offsetPositionRecordsForInsert
                offsetPositionRecordsForInsert(positionStart, itemCount);
                mItemsAddedOrRemoved = true;
            }

            @Override
            public void offsetPositionsForMove(int from, int to) {
                offsetPositionRecordsForMove(from, to);
                // should we create mItemsMoved ?
                mItemsAddedOrRemoved = true;
            }
        });
    }


    private void initChildrenHelper() {
        mChildHelper = new ChildHelper(new ChildHelper.Callback() {
            @Override
            public int getChildCount() {
                return RecyclerView.this.getChildCount();
            }

            @Override
            public void addView(View child, int index) {
                if (VERBOSE_TRACING) {
                    TraceCompat.beginSection("RV addView");
                }
                RecyclerView.this.addView(child, index);
                if (VERBOSE_TRACING) {
                    TraceCompat.endSection();
                }
                dispatchChildAttached(child);
            }

            @Override
            public int indexOfChild(View view) {
                return RecyclerView.this.indexOfChild(view);
            }

            @Override
            public void removeViewAt(int index) {
                final View child = RecyclerView.this.getChildAt(index);
                if (child != null) {
                    dispatchChildDetached(child);

                    // Clear any android.view.animation.Animation that may prevent the item from
                    // detaching when being removed. If a child is re-added before the
                    // lazy detach occurs, it will receive invalid attach/detach sequencing.
                    child.clearAnimation();
                }
                if (VERBOSE_TRACING) {
                    TraceCompat.beginSection("RV removeViewAt");
                }
                RecyclerView.this.removeViewAt(index);
                if (VERBOSE_TRACING) {
                    TraceCompat.endSection();
                }
            }
        });
    }
>RecyclerView.java
// (12) 该方法主要是遍历所有的ViewHolder,然后把在插入位置之后的ViewHolder的位置
// 向后移动插入的个数,最后在对Recycler中缓存的ViewHolder做同样的操作,最后申请重新布局。
    void offsetPositionRecordsForInsert(int positionStart, int itemCount) {
        final int childCount = mChildHelper.getUnfilteredChildCount();
        //遍历所有的ViewHolder
        for (int i = 0; i < childCount; i++) {
            final ViewHolder holder = getChildViewHolderInt(mChildHelper.getUnfilteredChildAt(i));
            if (holder != null && !holder.shouldIgnore() && holder.mPosition >= positionStart) {
                //插入位置之后的ViewHolder的位置向后移动插入的个数
                holder.offsetPosition(itemCount, false);
                mState.mStructureChanged = true;
            }
        }
        //对Recycler中缓存的ViewHolder,插入位置之后的ViewHolder的位置向后移动插入的个数
        mRecycler.offsetPositionRecordsForInsert(positionStart, itemCount);
        //重新请求布局
        requestLayout();
    }

>Recycler.java

void offsetPositionRecordsForInsert(int insertedAt, int count) {
    final int cachedCount = mCachedViews.size();
    for (int i = 0; i < cachedCount; i++) {
        final RecyclerView.ViewHolder holder = mCachedViews.get(i);
        if (holder != null && holder.mPosition >= insertedAt) {
            holder.offsetPosition(count, true);
        }
    }
}

布局流程到触发动画的过程

根据上面的分析,如果我们没有设置mHasFixedSize=true,那么我们最早在 (4) 步就会requestLayout,从而使View重新Layout,就是在那么现在我们再来看布局流程:

首先是step1:


private void dispatchLayoutStep1() {
    ……
    if (mState.mRunSimpleAnimations) {
        // 遍历所有显示着的ViewHolder,找到那些没有被移除的ViewHolder,储存在mViewInfoStore中,并找到那些只有内容更新的ViewHolder储存在mViewInfoStore中,前后存储的容器不同,然后准备执行之前的布局。mItemAnimator的recordPreLayoutInformation方法就是将ViewHolder的边界信息记录在ItemHolderInfo中。
        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());
            //存储布局改变的ViewHolder对应的动画信息
            mViewInfoStore.addToPreLayout(holder, animationInfo);

            //将只有内容更新的ViewHolder储存到mOldChangedHolders容器中
            if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()
                    && !holder.shouldIgnore() && !holder.isInvalid()) {
                long key = getChangedHolderKey(holder);
                mViewInfoStore.addToOldChangeHolders(key, holder);
            }
        }
    }
    if (mState.mRunPredictiveAnimations) {
        // 下面的内容是需要在布局结束之后运行动画的情况下执行的。主要做的事情就是
        // 执行上一布局操作,上一布局操作其实就是先以上一次的状态执行一边LayoutManager
        // 的onLayoutChildren方法,其实RecyclerView的布局策略就是在
        // LayoutManager的onLayoutChildren方法中。执行一次它就获得了所有
        // ViewHolder的边界信息。只不过,这次获得的是之前状态下的ViewHolder的
        // 边界信息。不过这个应该是要在LayoutManager中,根据state的isPreLayout
        // 的返回值,选择使用新的还是旧的position。但我在系统给的几个LayoutManager中
        // 都没有看到。
        saveOldPositions();
        final boolean didStructureChange = mState.mStructureChanged;
        mState.mStructureChanged = false;
        mLayout.onLayoutChildren(mRecycler, mState);
        mState.mStructureChanged = didStructureChange;
        // 遍历ViewHolder,判断动画标志,存储到mViewInfoStore
        for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
            final View child = mChildHelper.getChildAt(i);
            final ViewHolder viewHolder = getChildViewHolderInt(child);
            if (viewHolder.shouldIgnore()) {
                continue;
            }
            if (!mViewInfoStore.isInPreLayout(viewHolder)) {
                int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);
                boolean wasHidden = viewHolder
                        .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
                if (!wasHidden) {
                    flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
                }
                final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(
                        mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());
                if (wasHidden) {
                    recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
                } else {
                    mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
                }
            }
        }
        ……
    } 
    ……
}

这里我觉得有必要讲一个mViewInfoStore,mViewInfoStore是一个ViewInfoStore。来看一下这个类:这个类是用来追踪View所要做的动画的。其中有一个内部类InfoRecord,该类用来存储ViewHolder前后的信息,以及ViewHolder状态的flag。

其中有两个比较重要的成员变量,mLayoutHolderMap和mOldChangedHolders,分别用来存储我们的布局改变的ViewHolder对应的动画信息和内容改变的ViewHolder。

ViewInfoStore通过addToPostLayout,addToPreLayout……等方法添加ViewHolder到mLayoutHolderMap中,并且为其InfoRecord设置对应的flag。

在layout的过程中就是分析View的各种状态,然后使用对应的方法添加到ViewInfoStore中

ViewInfoStore中还有一个非常重要的方法,process,该方法会处理所有的mLayoutHolderMap中的值,并根据其flag和前后的信息来判断ViewHolder的动作,并将这个动作反应给ProcessCallback。分别有4种行为:消失,出现,一直存在,未使用。然后交给外面去处理。

class ViewInfoStore {
    final ArrayMap mLayoutHolderMap = new ArrayMap<>();

    final LongSparseArray mOldChangedHolders = new LongSparseArray<>();

    void addToPreLayout(ViewHolder holder, ItemHolderInfo info) { …… }
    
    void addToPostLayout(ViewHolder holder, ItemHolderInfo info) { …… }
    
    void process(ProcessCallback callback) {
        for (int index = mLayoutHolderMap.size() - 1; index >= 0; index --) {
            final ViewHolder viewHolder = mLayoutHolderMap.keyAt(index);
            final InfoRecord record = mLayoutHolderMap.removeAt(index);
            if ((record.flags & FLAG_APPEAR_AND_DISAPPEAR) == FLAG_APPEAR_AND_DISAPPEAR) {
                //出现然后消失了。对于动画没有用处。
                callback.unused(viewHolder);
            } else if ((record.flags & FLAG_DISAPPEARED) != 0) {
                //类似于出现消失,但发生在不同的布局通道之间。当布局管理器使用自动测量时,可能会发生这种情况
                if (record.preInfo == null) {
                    callback.unused(viewHolder);
                } else {//消失行为
                    callback.processDisappeared(viewHolder, record.preInfo, record.postInfo);
                }
            } else if ((record.flags & FLAG_APPEAR_PRE_AND_POST) == FLAG_APPEAR_PRE_AND_POST) {//出现在布局中,但没有出现在适配器中(例如,进入视窗)
                callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
            } else if ((record.flags & FLAG_PRE_AND_POST) == FLAG_PRE_AND_POST) {
                //一直存在
                callback.processPersistent(viewHolder, record.preInfo, record.postInfo);
            } else if ((record.flags & FLAG_PRE) != 0) {
                //在 pre-layout,从来没有添加到post layout
                callback.processDisappeared(viewHolder, record.preInfo, null);
            } else if ((record.flags & FLAG_POST) != 0) {
                //不在 pre-layout,添加到post layout
                callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
            } else if ((record.flags & FLAG_APPEAR) != 0) {
                //取消的观点。RecyclerView将处理移除/回收这个。
            } else if (DEBUG) {
                throw new IllegalStateException("record without any reasonable flag combination:/");
            }
            InfoRecord.recycle(record);
        }
    }
    
    interface ProcessCallback {
        void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo preInfo,
                @Nullable ItemHolderInfo postInfo);
        void processAppeared(ViewHolder viewHolder, @Nullable ItemHolderInfo preInfo,
                ItemHolderInfo postInfo);
        void processPersistent(ViewHolder viewHolder, @NonNull ItemHolderInfo preInfo,
                @NonNull ItemHolderInfo postInfo);
        void unused(ViewHolder holder);
    }

    static class InfoRecord {
        ……
        int flags;
        @Nullable ItemHolderInfo preInfo;
        @Nullable ItemHolderInfo postInfo;
        ……
    }
}

好,简单了解一下ViewInfoStore之后,继续看布局流程。dispatchLayoutStep2中没有对动画的相关处理,直接看第三步。在判断里面,我不去一行一行的讲了,它们就是遍历所有的ViewHolder,然后判断各种情况,最后添加到mViewInfoStore中。遍历结束之后执行mViewInfoStore的process处理所有的在其mLayoutHolderMap中的ViewHolder。

private void dispatchLayoutStep3() {
    if (mState.mRunSimpleAnimations) {
        for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
            ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));
            if (holder.shouldIgnore()) {
                continue;
            }
            long key = getChangedHolderKey(holder);
            final ItemHolderInfo animationInfo = mItemAnimator
                    .recordPostLayoutInformation(mState, holder);
            ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
            if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
                final boolean oldDisappearing = mViewInfoStore.isDisappearing(
                        oldChangeViewHolder);
                final boolean newDisappearing = mViewInfoStore.isDisappearing(holder);
                if (oldDisappearing && oldChangeViewHolder == holder) {
                    mViewInfoStore.addToPostLayout(holder, animationInfo);
                } else {
                    final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(
                            oldChangeViewHolder);
                    mViewInfoStore.addToPostLayout(holder, animationInfo);
                    ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
                    if (preInfo == null) {
                        handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder);
                    } else {
                        animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
                                oldDisappearing, newDisappearing);
                    }
                }
            } else {
                mViewInfoStore.addToPostLayout(holder, animationInfo);
            }
        }

        // Step 4: Process view info lists and trigger animations
        mViewInfoStore.process(mViewInfoProcessCallback);
    }
    ……
}

之后我们看一下mViewInfoStore的ProcessCallback的实现mViewInfoProcessCallback,这里只拿processAppeared做分析:

>RecyclerView.java
    private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
            new ViewInfoStore.ProcessCallback() {
                @Override
                public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
                        @Nullable ItemHolderInfo postInfo) {
                    mRecycler.unscrapView(viewHolder);
                    animateDisappearance(viewHolder, info, postInfo);
                }
                @Override
                public void processAppeared(ViewHolder viewHolder,
                        ItemHolderInfo preInfo, ItemHolderInfo info) {
                    //继续看
                    animateAppearance(viewHolder, preInfo, info);
                }

                @Override
                public void processPersistent(ViewHolder viewHolder,
                        @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
                    viewHolder.setIsRecyclable(false);
                    if (mDataSetHasChangedAfterLayout) {
                        // since it was rebound, use change instead as we'll be mapping them from
                        // stable ids. If stable ids were false, we would not be running any
                        // animations
                        if (mItemAnimator.animateChange(viewHolder, viewHolder, preInfo,
                                postInfo)) {
                            postAnimationRunner();
                        }
                    } else if (mItemAnimator.animatePersistence(viewHolder, preInfo, postInfo)) {
                        postAnimationRunner();
                    }
                }
                @Override
                public void unused(ViewHolder viewHolder) {
                    mLayout.removeAndRecycleView(viewHolder.itemView, mRecycler);
                }
            };
private void animateAppearance(@NonNull ViewHolder itemHolder,
        @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
    itemHolder.setIsRecyclable(false);
    if (mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)) {
        postAnimationRunner();
    }
}

该方法中不要忽略if中的内容:mItemAnimator.animateAppearance(itemHolder, preLayoutInfo, postLayoutInfo)
那么进入该方法:看注释。

@Override
public boolean animateAppearance(@NonNull ViewHolder viewHolder,
        @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
    // 该方法通过前后的布局信息来判断是移动还是添加。下面我们以添加为例分析
    if (preLayoutInfo != null && (preLayoutInfo.left != postLayoutInfo.left
            || preLayoutInfo.top != postLayoutInfo.top)) {
        return animateMove(viewHolder, preLayoutInfo.left, preLayoutInfo.top,
                postLayoutInfo.left, postLayoutInfo.top);
    } else {
        return animateAdd(viewHolder);
    }
}

真正实现是在DefaultItenAnimator中:这里做了三件事情,重置holder的动画,设置显示属性,然后添加到mPendingAdditions中,mPendingAdditions是一个存储添加ViewHolder的List,表示待处理的添加动画的ViewHolder。同样在DefaultItenAnimator中也有,移动的,移除的列表。

@Override
public boolean animateAdd(final ViewHolder holder) {
    resetAnimation(holder);
    ViewCompat.setAlpha(holder.itemView, 0);
    mPendingAdditions.add(holder);
    return true;
}

最后返回true,进入if,执行postAnimationRunner方法。

private void postAnimationRunner() {
    if (!mPostedAnimatorRunner && mIsAttached) {
        ViewCompat.postOnAnimation(this, mItemAnimatorRunner);
        mPostedAnimatorRunner = true;
    }
}

 

你可能感兴趣的:(android,android)