
前言
抽絲剝繭 RecyclerView系列文章的目的在于幫助Android開發(fā)者提高對(duì)RecyclerView的認(rèn)知,本文是整個(gè)系列的第三篇。
在前面的系列文章中,我們從源碼的角度分別研究了:
-
RecyclerView(整體結(jié)構(gòu)) RecyclerLayoutManger-
ItemDecoration(略微了解)
縱觀RecyclerView,似乎還剩下ItemAnimator和Adapter,那么本文作為抽絲剝繭RecyclerView系列的最后一篇,自然要將剩下的部分全部分析完畢(文末有往期文章的鏈接)。
目錄

一、RecyclerView中的魔法師 - Adapter
我將Adapter稱為RecyclerView中的魔法師,為什么叫它魔法師呢?因?yàn)樗鼘?shù)據(jù)變成了具體的視圖,不過(guò)這也是我們平時(shí)談?wù)擃H多的適配器模式。
Adapter的主要功能是數(shù)據(jù)轉(zhuǎn)子視圖和數(shù)據(jù)管理及通知,所以在了解源碼之前,我們還需了解Adpater的相關(guān)類:
| 名稱 | 作用 |
|---|---|
AdapterDataObservable |
數(shù)據(jù)發(fā)生變化的時(shí)候?qū)嶋H處理的類 |
ViewHolder |
存入子視圖和當(dāng)前的位置信息,大家應(yīng)該都很熟悉了~ |
1. 數(shù)據(jù)轉(zhuǎn)子視圖
在之前的文章《抽絲剝繭RecyclerView - 化整為零》我們介紹Recycler的時(shí)候,已經(jīng)了解到在Recycler如果沒有緩存ViewHolder,會(huì)調(diào)用Adapter#onCreateViewHolder創(chuàng)建一個(gè)ViewHolder,我們平常在該方法的實(shí)現(xiàn)中,通常會(huì):
View root = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.xxx,viewGroup,false);
return new ViewHolder(root);
以這樣的方式創(chuàng)建子視圖,創(chuàng)建完的子視圖會(huì)交給ViewHolder管理,存儲(chǔ)在ViewHolder中的itemView,接著Recycler會(huì)調(diào)用Adapter#onBindViewHolder實(shí)現(xiàn)將數(shù)據(jù)展示在控件中,不過(guò)這兩個(gè)方法都是由控件的使用者實(shí)現(xiàn)。
2. 數(shù)據(jù)管理
每次數(shù)據(jù)發(fā)生變化的時(shí)候,我們都需要調(diào)用Adapter#notifyxxx通知RecyclerView數(shù)據(jù)集發(fā)生了變化。這次我們以刪除為例來(lái)分析源碼。
2.1 設(shè)置適配器
設(shè)置適配器的代碼是RecyclerView#setAdapter:
public void setAdapter(Adapter adapter) {
// ...
// 重點(diǎn)方法
setAdapterInternal(adapter, false, true);
// ...
}
private void setAdapterInternal(Adapter adapter, Boolean compatibleWithPrevious,
Boolean removeAndRecycleViews) {
if (mAdapter != null) {
// 舊的適配器解除注冊(cè)
mAdapter.unregisterAdapterDataObserver(mObserver);
mAdapter.onDetachedFromRecyclerView(this);
}
// ...
final Adapter oldAdapter = mAdapter;
// 對(duì)新的適配器檢測(cè)數(shù)據(jù)監(jiān)聽
mAdapter = adapter;
if (adapter != null) {
adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this);
}
// ...
}
該代碼主要作用有兩點(diǎn):
- 舊的適配器取消注冊(cè)
- 注冊(cè)新的適配器中的數(shù)據(jù)變化的通知對(duì)象
數(shù)據(jù)變化通知對(duì)象是這個(gè)mObserver,來(lái)看看這個(gè)mObserver是什么:
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
// 數(shù)據(jù)發(fā)生變化的回調(diào)接口
// 通知到RecyclerView中
// RecyclerViewDataObserver繼承自AdapterDataObserver
public abstract static class AdapterDataObserver {
public void onChanged() {
// Do nothing
}
public void onItemRangeChanged(int positionStart, int itemCount) {
// do nothing
}
public void onItemRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
// fallback to onItemRangeChanged(positionStart, itemCount) if app
// does not override this method.
onItemRangeChanged(positionStart, itemCount);
}
public void onItemRangeInserted(int positionStart, int itemCount) {
// do nothing
}
public void onItemRangeRemoved(int positionStart, int itemCount) {
// do nothing
}
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
// do nothing
}
}
而這個(gè)RecyclerViewDataObserver則是繼承自AdapterDataObserver抽象類,具體的實(shí)現(xiàn)細(xì)節(jié)我們后面再討論。
2.2 數(shù)據(jù)刪除
使用場(chǎng)景是這樣的:

點(diǎn)擊界面中的一個(gè)刪除按鈕,刪除數(shù)據(jù)列表中的第一個(gè)數(shù)據(jù),然后使用適配器通知數(shù)據(jù)中已經(jīng)刪除:
btnDeleteOne.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
List<String> strings = mAdapter.getValues();
if(strings.size() == 0)
return;
// 移除第一個(gè)數(shù)據(jù)
strings.remove(0);
// 適配器通知?jiǎng)h除
mAdapter.notifyItemRemoved(0);
}
2.3 適配器通知
這里有必要說(shuō)明一下:Adapter和RecyclerViewDataObserver都是RecyclerView的內(nèi)部類,所以它們可以直接使用RecyclerView內(nèi)部的資源。
當(dāng)RecyclerView中的數(shù)據(jù)刪除的時(shí)候,我們調(diào)用了Adapter#notifyRemoved方法:
public final void notifyItemRemoved(int position) {
mObservable.notifyItemRangeRemoved(position, 1);
}
發(fā)現(xiàn)刪除的處理交給了上面介紹的mObservable,我們來(lái)看一下RecyclerViewDataObserver#notifyItemRemoved具體實(shí)現(xiàn):
private class RecyclerViewDataObserver extends AdapterDataObserver {
//... 省略一些方法
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
triggerUpdateProcessor();
}
}
// 刷新界面或者直接進(jìn)行動(dòng)畫
// 刪除這里是調(diào)用的刷新界面
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
}
在RecyclerViewDataObserver的通知?jiǎng)h除方法中,它又把刪除的處理交給了AdapterHelper,調(diào)用了AdapterHelper#onItemRangeRemoved:
/**
* @return True if updates should be processed.
*/
Boolean onItemRangeRemoved(int positionStart, int itemCount) {
if (itemCount < 1) {
return false;
}
// mPendingUpdates是List<UpdateOp>
// 這里是將一個(gè)刪除的UpdateOp加入mPendingUpdates中
mPendingUpdates.add(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount, null));
mExistingUpdateTypes |= UpdateOp.REMOVE;
return mPendingUpdates.size() == 1;
}
AdapterHelper對(duì)自己比較有信心,沒有交給別人處理,他在自己的mPendingUpdates中加入一個(gè)刪除標(biāo)記的UpdateOp,這個(gè)mPendingUpdates有什么作用呢?我們同樣在使用的時(shí)候介紹?;氐?code>RecyclerViewDataObserver中的RecyclerViewDataObserver#notifyItemRemoved,調(diào)用完AdapterHelper#onItemRangeRemoved之后,它立馬又調(diào)用了requestLayout進(jìn)行界面刷新。
目錄.png

2.4 界面繪制流程的一些細(xì)節(jié)
界面繪制一直是我們之前博客的重點(diǎn)討論對(duì)象,本章我們就數(shù)據(jù)通知再看一下關(guān)于數(shù)據(jù)通知的細(xì)節(jié)。
在RecyclerView#dispatchLayoutStep1方法中,RecyclerView會(huì)調(diào)用RecyclerView#processAdapterUpdatesAndSetAnimationFlags處理Adapter中的更新和為動(dòng)畫設(shè)置標(biāo)記,這里我們只看適配器數(shù)據(jù)更新相關(guān):
private void processAdapterUpdatesAndSetAnimationFlags() {
//...
// simple animations are a subset of advanced animations (which will cause a
// pre-layout step)
// If layout supports predictive animations, pre-process to decide if we want to run them
if (predictiveItemAnimationsEnabled()) {
mAdapterHelper.preProcess();
} else {
mAdapterHelper.consumeUpdatesInOnePass();
}
// ...
}
private Boolean predictiveItemAnimationsEnabled() {
// RecyclerView設(shè)置了默認(rèn)的mItemAnimator,
// 以及LinearLayout的supportsPredictiveItemAnimations()為true
// 該方法返回為true
return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
}
由于RecyclerView#predictiveItemAnimationsEnabled通常會(huì)返回true,那我們跳到AdapterHelper,查看AdapterHelper#preProcess方法:
void preProcess() {
// ...
final int count = mPendingUpdates.size();
for (int i = 0; i < count; i++) {
UpdateOp op = mPendingUpdates.get(i);
switch (op.cmd) {
// ... 添加省略
case UpdateOp.REMOVE:
applyRemove(op);
break;
// 更新、移動(dòng)標(biāo)簽省略
}
}
mPendingUpdates.clear();
}
mPendingUpdates是一個(gè)ArrayList<UpdateOp>,上述方法就是消費(fèi)我們?cè)谥疤砑舆M(jìn)mPendingUpdates的刪除UpdateOp,在處理刪除屬性的UpdateOp的AdapterHelper#applyRemove方法中又調(diào)用AdapterHelper#postponeAndUpdateViewHolders:
private void postponeAndUpdateViewHolders(UpdateOp op) {
mPostponedList.add(op);
switch (op.cmd) {
// ... 省略添加、更新、移動(dòng)
case UpdateOp.REMOVE:
mCallback.offsetPositionsForRemovingLaidOutOrNewView(op.positionStart,op.itemCount);
break;
default:
throw new IllegalArgumentException("Unknown update op type for " + op);
}
}
真實(shí)的處理交給了AdapterHelper中的mCallback,而mCallback的實(shí)現(xiàn)同樣也在RecyclerView,那我們直接查看mCallback的具體實(shí)現(xiàn):
void initAdapterManager() {
mAdapterHelper = new AdapterHelper(new AdapterHelper.Callback() {
// ...省略其他方法
// 僅僅展示刪除的方法
@Override
public void offsetPositionsForRemovingLaidOutOrNewView(
int positionStart, int itemCount) {
offsetPositionRecordsForRemove(positionStart, itemCount, false);
mItemsAddedOrRemoved = true;
}
//... 省略其他方法
});
}
void offsetPositionRecordsForRemove(int positionStart, int itemCount,
Boolean applyToPreLayout) {
final int positionEnd = positionStart + itemCount;
final int childCount = mChildHelper.getUnfilteredChildCount();
for (int i = 0; i < childCount; i++) {
final ViewHolder holder = getChildViewHolderint(mChildHelper.getUnfilteredChildAt(i));
if (holder != null && !holder.shouldIgnore()) {
if (holder.mPosition >= positionEnd) {
// 更新未刪除的ViewHOlder的的位置信息
holder.offsetPosition(-itemCount, applyToPreLayout);
mState.mStructureChanged = true;
} else if (holder.mPosition >= positionStart) {
// 跟新要?jiǎng)h除逇ViewHolder的位置信息
holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
applyToPreLayout);
mState.mStructureChanged = true;
}
}
}
mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
requestLayout();
}
上述代碼的作用主要有兩點(diǎn):
- 對(duì)于要?jiǎng)h除的
ViewHolder加上刪除的flag,更新ViewHolder的位置 - 對(duì)于位置會(huì)變化的
ViewHolder則更新位置
在數(shù)據(jù)刪除以后,Adapter的作用就是為這些變化的ViewHolder添加刪除標(biāo)簽和更新位置信息,后續(xù)的處理就交給了LayoutManager和ItemAnimator,我們?cè)谙旅娴膭?dòng)畫中分析~
二、界面交互的粘合劑 - ItemAnimator
好的動(dòng)畫會(huì)讓界面的交互很自然,RecyclerView作為一款強(qiáng)大的UI控件,自然也是支持動(dòng)畫的,沒錯(cuò),RecyclerView子視圖動(dòng)畫是由ItemAnimator實(shí)現(xiàn)的。
上文中的Gif不適合講解,于是我換了一張聊天圖,同樣要?jiǎng)h除第一條信息:

上文中,我們討論
Adapter的結(jié)果是它更新了ViewHolder的一些flag,那么這些有了flag的ViewHolder是如何處理的呢?
在此之前,簡(jiǎn)單了解一下動(dòng)畫相關(guān)類ViewInfoStore:

1. 進(jìn)行預(yù)布局
預(yù)布局是什么呢?簡(jiǎn)單來(lái)說(shuō),RecyclerView進(jìn)行真實(shí)的布局之前,提前進(jìn)行一次布局,也就是說(shuō),LayoutManager#onLayoutChildren方法會(huì)執(zhí)行兩次,那么為什么會(huì)執(zhí)行兩次呢?我們慢慢分析。
預(yù)布局是一個(gè)很重要得過(guò)程,當(dāng)有簡(jiǎn)單的子視圖動(dòng)畫發(fā)生的時(shí)候,它就會(huì)被觸發(fā),這一點(diǎn)我們得回顧一下RecyclerView#dispatchLayoutStep1方法,直接進(jìn)入其中的RecyclerView#processAdapterUpdatesAndSetAnimationFlags方法:
private void processAdapterUpdatesAndSetAnimationFlags() {
// simple animations are a subset of advanced animations (which will cause a
// pre-layout step)
// If layout supports predictive animations, pre-process to decide if we want to run them
if (predictiveItemAnimationsEnabled()) {
mAdapterHelper.preProcess();
} else {
mAdapterHelper.consumeUpdatesInOnePass();
}
Boolean animationTypeSupported = mItemsAddedOrRemoved || mItemsChanged;
// mFirstLayoutComplete會(huì)在第一次布局完成以后設(shè)置為true
mState.mRunSimpleAnimations = mFirstLayoutComplete
&& mItemAnimator != null
&& (mDataSetHasChangedAfterLayout
|| animationTypeSupported
|| mLayout.mRequestedSimpleAnimations)
&& (!mDataSetHasChangedAfterLayout
|| mAdapter.hasStableIds());
mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
&& animationTypeSupported
&& !mDataSetHasChangedAfterLayout
&& predictiveItemAnimationsEnabled();
}
從上面的代碼中,我們可以看出:
- 第一段注釋中表明,簡(jiǎn)單的動(dòng)畫會(huì)觸發(fā)預(yù)布局
- 當(dāng)
RecyclerView第一次布局完成以后才有資格觸發(fā)動(dòng)畫,mFirstLayoutComplete是在第一次布局完成以后設(shè)置為true -
mState.mRunSimpleAnimations為true是mState.mRunPredictiveAnimations為true的充要條件,mState.mRunPredictiveAnimations這個(gè)屬性很重要,由它決定是否進(jìn)行預(yù)布局
重新回到RecyclerView#dispatchLayoutStep1方法:
private void dispatchLayoutStep1() {
// ...
mViewInfoStore.clear();
processAdapterUpdatesAndSetAnimationFlags();
// 重置一些狀態(tài)
// ... 省略
mItemsAddedOrRemoved = mItemsChanged = false;
// 是否預(yù)布局取決于mState.mRunPredictiveAnimations
mState.mInPreLayout = mState.mRunPredictiveAnimations;
if (mState.mRunSimpleAnimations) {
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;
}
// 記錄當(dāng)前的位置信息 Left、Right、Top、Bottom等
final ItemHolderInfo animationInfo = mItemAnimator
.recordPreLayoutInformation(mState, holder,
ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),
holder.getUnmodifiedPayloads());
mViewInfoStore.addToPreLayout(holder, animationInfo);
// ... 省略
}
}
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.
// 大致就是layoutManager會(huì)layout每一個(gè)子視圖,包括后面加入的子視圖和刪除的子視圖,這樣以后,layoutManager就很清楚
// 要執(zhí)行哪些動(dòng)畫了
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 (!mViewInfoStore.isInPreLayout(viewHolder)) {
// 對(duì)于新出來(lái)的ViewHolder添加標(biāo)簽
// ... 省略一些方法
mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
}
}
// we don't process disappearing list because they may re-appear in post layout pass.
clearOldPositions();
} else {
clearOldPositions();
}
// ...
}
除了上面直接進(jìn)入的方法,還有兩個(gè)if語(yǔ)句。
第一個(gè)if語(yǔ)句:mState.mRunSimpleAnimations為true
這個(gè)內(nèi)容很簡(jiǎn)單,對(duì)預(yù)布局前存在的ViewHolder的的位置信息進(jìn)行記錄。
第二個(gè)if語(yǔ)句:mState.mRunPredictiveAnimations為true
第二個(gè)if語(yǔ)句的內(nèi)容就復(fù)雜多了,首先會(huì)進(jìn)行預(yù)布局過(guò)程,該過(guò)程第一次調(diào)用了LayoutManager#onLayoutChildren,關(guān)于布局的具體過(guò)程,這里我就不講解了,想要了解的同學(xué)可以翻閱我之前的文章:《抽絲剝繭RecyclerView - LayoutManager》。
需要指出的是,在添加子視圖中,調(diào)用了LayoutManager#addViewInt方法:
private void addViewint(View child, int index, Boolean disappearing) {
// ...
if (disappearing || holder.isRemoved()) {
// these views will be hidden at the end of the layout pass.
mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
} else {
// This may look like unnecessary but may happen if layout manager supports
// predictive layouts and adapter removed then re-added the same item.
// In this case, added version will be visible in the post layout (because add is
// deferred) but RV will still bind it to the same View.
// So if a View re-appears in post layout pass, remove it from disappearing list.
mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
}
// ...
}
該方法的目的是如果是被刪除的ViewHolder,它會(huì)為ViewInfoStore中ViewHolder對(duì)應(yīng)的記錄InfoRecord添加已刪除的標(biāo)記,在真實(shí)的布局(非預(yù)布局)中,被刪除的ViewHolder是不會(huì)被使用的,所以說(shuō),只有預(yù)布局才會(huì)記錄刪除動(dòng)畫。

可以看到,第一次布局完了以后,需要?jiǎng)h除的ViewHolder和自動(dòng)填充的ViewHolder都被加入了RecyclerView,不過(guò),RecyclerView#DispatchLayoutStep1還沒結(jié)束,它會(huì)調(diào)用ViewInfoStore會(huì)給新加入的ViewHolder添加對(duì)應(yīng)的InfoRecord。
完成這個(gè)以后,RecyclerView對(duì)于要處理哪些動(dòng)畫就了如指掌了,這個(gè)也是預(yù)布局的意義。
2. 真實(shí)布局
同樣不講具體的代碼,第二次布局完成以后,界面變成了:

看到上面的圖,你可能會(huì)有這樣的疑問(wèn),為什么要?jiǎng)h除的子視圖沒了?說(shuō)好不講代碼的,打臉,只能甩出一段緩存
Recycler的源碼了??:
ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, Boolean dryRun) {
final int scrapCount = mAttachedScrap.size();
// Try first for an exact, non-invalid match from scrap.
for (int i = 0; i < scrapCount; i++) {
final ViewHolder holder = mAttachedScrap.get(i);
if (... && (mState.mInPreLayout || !holder.isRemoved())) {
// 在第一級(jí)緩存mAttachedScrap中,如果是刪除的ViewHolder
// 預(yù)布局是可以使用的,真實(shí)布局不可以使用
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
//...
return null;
}
雖然解決了當(dāng)前的疑問(wèn),你可能還會(huì)有另外一個(gè)疑問(wèn),沒有了被刪除的子視圖,刪除動(dòng)畫還怎么執(zhí)行呢?我們還是先看看接下來(lái)的過(guò)程吧。
3. 執(zhí)行動(dòng)畫
之前我們記錄了那么多ViewHolder中子視圖的信息,現(xiàn)在到了使用的時(shí)候了:
private void dispatchLayoutStep3() {
// ...
if (mState.mRunSimpleAnimations) {
// Step 3: Find out where things are now, and process change animations.
// 找到當(dāng)前的ViewHolder,執(zhí)行需要執(zhí)行的動(dòng)畫
for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
ViewHolder holder = getChildViewHolderint(mChildHelper.getChildAt(i));
long key = getChangedHolderKey(holder);
final ItemHolderInfo animationInfo = mItemAnimator
.recordPostLayoutInformation(mState, holder);
ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);
if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) {
// ...
if (oldDisappearing && oldChangeViewHolder == holder) {
// run disappear animation instead of change
mViewInfoStore.addToPostLayout(holder, animationInfo);
} else {
// ...
mViewInfoStore.addToPostLayout(holder, animationInfo);
ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder);
// ...
}
} else {
mViewInfoStore.addToPostLayout(holder, animationInfo);
}
}
// Step 4: Process view info lists and trigger animations
// 執(zhí)行動(dòng)畫
mViewInfoStore.process(mViewInfoProcessCallback);
}
// 重置一些跟動(dòng)畫有關(guān)的類
// ...
mState.mRunSimpleAnimations = false;
mState.mRunPredictiveAnimations = false;
// ...
mViewInfoStore.clear();
}
這個(gè)函數(shù)的上半部分主要的目的是為了給ViewInfoStore里的ViewHolder相關(guān)的InfoRecord添加Post標(biāo)簽,下半部分mViewInfoStore.process(mViewInfoProcessCallback)則是我們的核心功能 - 動(dòng)畫執(zhí)行,我們重點(diǎn)看一下這個(gè)方法:
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);
// 根據(jù)不同的Flag執(zhí)行不同的動(dòng)畫
if ((record.flags & FLAG_APPEAR_AND_DISAPPEAR) == FLAG_APPEAR_AND_DISAPPEAR) {
callback.unused(viewHolder);
} else if ((record.flags & FLAG_DISAPPEARED) != 0) {
// Set as "disappeared" by the LayoutManager (addDisappearingView)
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) {
// Appeared in the layout but not in the adapter (e.g. entered the viewport)
callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
} else if ((record.flags & FLAG_PRE_AND_POST) == FLAG_PRE_AND_POST) {
// Persistent in both passes. Animate persistence
callback.processPersistent(viewHolder, record.preInfo, record.postInfo);
} else if ((record.flags & FLAG_PRE) != 0) {
// Was in pre-layout, never been added to post layout
callback.processDisappeared(viewHolder, record.preInfo, null);
} else if ((record.flags & FLAG_POST) != 0) {
// Was not in pre-layout, been added to post layout
callback.processAppeared(viewHolder, record.preInfo, record.postInfo);
} else if ((record.flags & FLAG_APPEAR) != 0) {
// Scrap view. RecyclerView will handle removing/recycling this.
} else if (DEBUG) {
throw new IllegalStateException("record without any reasonable flag combination:/");
}
InfoRecord.recycle(record);
}
}
// 回調(diào)接口
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);
}
在ViewInfoStore#process這個(gè)關(guān)鍵方法中,遍歷mLayoutHolderMap獲取ViewHolder綁定的InfoRecord,根據(jù)不同flag的InfoRecord,回調(diào)不同的方法,進(jìn)而處理不同的動(dòng)畫,回調(diào)接口的實(shí)現(xiàn)在RecyclerView中:
/**
* The callback to convert view info diffs into animations.
*/
private final ViewInfoStore.ProcessCallback mViewInfoProcessCallback =
new ViewInfoStore.ProcessCallback() {
@Override
public void processDisappeared(ViewHolder viewHolder, @NonNull ItemHolderInfo info,
@Nullable ItemHolderInfo postInfo) {
// 先移除緩存中的ViewHolder
mRecycler.unscrapView(viewHolder);
animateDisappearance(viewHolder, info, postInfo);
}
@Override
public void processAppeared(ViewHolder viewHolder,
ItemHolderInfo preInfo, ItemHolderInfo info) {
// 出現(xiàn)的動(dòng)畫
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);
}
};
如果你注意到刪除方法,你的疑問(wèn)就更大了,刪除的子視圖都沒了,還執(zhí)行毛線刪除動(dòng)畫?那我就得告訴你了:雖然當(dāng)前RecyclerView沒有需要?jiǎng)h除的子視圖,但是當(dāng)前的ViewInfoStore有ViewHolder啊,所以在執(zhí)行刪除動(dòng)畫前會(huì)將ViewHolder中的子視圖重新添加到RecyclerView里面,這里看一下上面的processDisappeared方法調(diào)用的RecyclerView#animateDisappearance方法,來(lái)看看是不是這樣的:
void animateDisappearance(@NonNull ViewHolder holder,
@NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
// 將子視圖重新加入到界面
addAnimatingView(holder);
holder.setIsRecyclable(false);
// mItemAnimator執(zhí)行刪除動(dòng)畫
if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
postAnimationRunner();
}
}
/**
* 將刪除的子視圖重新添加進(jìn)界面
*/
private void addAnimatingView(ViewHolder viewHolder) {
final View view = viewHolder.itemView;
final Boolean alreadyParented = view.getParent() == this;
mRecycler.unscrapView(getChildViewHolder(view));
if (viewHolder.isTmpDetached()) {
// 重新attach回界面
mChildHelper.attachViewToParent(view, -1, view.getLayoutParams(), true);
} else if (!alreadyParented) {
// 添加視圖
mChildHelper.addView(view, true);
} else {
mChildHelper.hide(view);
}
}
一圖了解當(dāng)前界面ViewHolder的狀態(tài)和需要執(zhí)行的動(dòng)畫:

如上面代碼塊中所看到的,刪除子視圖的動(dòng)畫實(shí)際的執(zhí)行者是
mItemAnimator,其他動(dòng)畫也是如此。
4. DefaultItemAnimator機(jī)制
ItemAnimator是一個(gè)抽象類,所以一些方法需要具體的類實(shí)現(xiàn),在沒有指定具體的ItemAnimator情況下,系統(tǒng)使用了默認(rèn)的DefaultItemAnimator。一圖簡(jiǎn)單了解DefaultItemAnimator機(jī)制:

除了
DefaultItemAnimator,你還可以自定義一個(gè)ItemAnimator,主要實(shí)現(xiàn)增、刪、更新和移動(dòng)等一些方法,本文就不再深入了,感興趣的同學(xué)可以自行研究。
在DefaultItemAnimator的刪除動(dòng)畫中,會(huì)對(duì)被刪除的子視圖執(zhí)行透明度1-0的動(dòng)畫,動(dòng)畫結(jié)束后,會(huì)刪除子視圖和回收ViewHolder,位移動(dòng)畫沒有放在透明度動(dòng)畫結(jié)束后調(diào)用,而是使用時(shí)間為透明度動(dòng)畫執(zhí)行時(shí)間的延遲,所以看上去就像子視圖被刪除后下面的子視圖才開始網(wǎng)上位移的。
動(dòng)畫執(zhí)行完畢以后,圖片就變成了:

以上就是RecyclerView刪除部分的Adapter和ItemAnimator的調(diào)用原理,其他方法同學(xué)們可以自行分析~
三、總結(jié)

還是那句話,沒有一遍調(diào)試解決不了源碼閱讀,如果有,那就是兩遍??~,抽絲剝繭RecyclerView源碼分析到此就結(jié)束了。
如果你想繼續(xù)了解RecyclcerView:
第一篇:《抽絲剝繭RecyclerView - 化整為零》
第二篇:《抽絲剝繭RecyclerView - LayoutManager》
特別分享篇: