上一篇文章讲了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通知流程的图:
Adapter的通知过程是一个观察者模式。结合源码:
public static abstract class Adapter<VH extends ViewHolder> {
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方法最终调用的地方。它主要做了:
- 如果之前存在Adapter,先移除原来的,注销观察者,和从RecyclerView Detached。
- 然后根据参数,决定是否清除原来的ViewHolder
- 然后重置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<UpdateOp> mPendingUpdates = new ArrayList<UpdateOp>();
// (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<ViewHolder, InfoRecord> mLayoutHolderMap = new ArrayMap<>();
final LongSparseArray<ViewHolder> 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;
}
}