抽絲剝繭 RecyclerView
系列文章的目的在於幫助Android開發者提升對RecyclerView的認知,本文是整個系列的第三篇。緩存
在前面的系列文章中,咱們從源碼的角度分別研究了:bash
RecyclerView
(總體結構)Recycler
LayoutManger
ItemDecoration
(略微瞭解)縱觀RecyclerView
,彷佛還剩下ItemAnimator
和Adapter
,那麼本文做爲**抽絲剝繭RecyclerView
**系列的最後一篇,天然要將剩下的部分所有分析完畢(文末有往期文章的連接)。app
我將Adapter
稱爲RecyclerView
中的魔法師,爲何叫它魔法師呢?由於它將數據變成了具體的視圖,不過這也是咱們平時談論頗多的適配器模式。ide
Adapter
的主要功能是數據轉子視圖和數據管理及通知,因此在瞭解源碼以前,咱們還需瞭解Adpater
的相關類:函數
名稱 | 做用 |
---|---|
AdapterDataObservable |
數據發生變化的時候實際處理的類 |
ViewHolder |
存入子視圖 和當前的位置信息,你們應該都很熟悉了~ |
在以前的文章《抽絲剝繭RecyclerView - 化整爲零》咱們介紹Recycler
的時候,已經瞭解到在Recycler
若是沒有緩存ViewHolder
,會調用Adapter#onCreateViewHolder
建立一個ViewHolder
,咱們日常在該方法的實現中,一般會:源碼分析
View root = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.xxx,viewGroup,false);
return new ViewHolder(root);
複製代碼
以這樣的方式建立子視圖
,建立完的子視圖
會交給ViewHolder
管理,存儲在ViewHolder中的itemView
,接着Recycler
會調用Adapter#onBindViewHolder
實現將數據展現在控件中,不過這兩個方法都是由控件的使用者實現。佈局
每次數據發生變化的時候,咱們都須要調用Adapter#notifyxxx
通知RecyclerView
數據集發生了變化。此次咱們以刪除爲例來分析源碼。post
設置適配器的代碼是RecyclerView#setAdapter
:動畫
public void setAdapter(Adapter adapter) {
// ...
// 重點方法
setAdapterInternal(adapter, false, true);
// ...
}
private void setAdapterInternal(Adapter adapter, Boolean compatibleWithPrevious,
Boolean removeAndRecycleViews) {
if (mAdapter != null) {
// 舊的適配器解除註冊
mAdapter.unregisterAdapterDataObserver(mObserver);
mAdapter.onDetachedFromRecyclerView(this);
}
// ...
final Adapter oldAdapter = mAdapter;
// 對新的適配器檢測數據監聽
mAdapter = adapter;
if (adapter != null) {
adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this);
}
// ...
}
複製代碼
該代碼主要做用有兩點:ui
數據變化通知對象是這個mObserver
,來看看這個mObserver
是什麼:
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
// 數據發生變化的回調接口
// 通知到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
}
}
複製代碼
而這個RecyclerViewDataObserver
則是繼承自AdapterDataObserver
抽象類,具體的實現細節咱們後面再討論。
使用場景是這樣的:
btnDeleteOne.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
List<String> strings = mAdapter.getValues();
if(strings.size() == 0)
return;
// 移除第一個數據
strings.remove(0);
// 適配器通知刪除
mAdapter.notifyItemRemoved(0);
}
複製代碼
這裏有必要說明一下:Adapter
和RecyclerViewDataObserver
都是RecyclerView
的內部類,因此它們能夠直接使用RecyclerView
內部的資源。
當RecyclerView
中的數據刪除的時候,咱們調用了Adapter#notifyRemoved
方法:
public final void notifyItemRemoved(int position) {
mObservable.notifyItemRangeRemoved(position, 1);
}
複製代碼
發現刪除的處理交給了上面介紹的mObservable
,咱們來看一下RecyclerViewDataObserver#notifyItemRemoved
具體實現:
private class RecyclerViewDataObserver extends AdapterDataObserver {
//... 省略一些方法
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
triggerUpdateProcessor();
}
}
// 刷新界面或者直接進行動畫
// 刪除這裏是調用的刷新界面
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
}
複製代碼
在RecyclerViewDataObserver
的通知刪除方法中,它又把刪除的處理交給了AdapterHelper
,調用了AdapterHelper#onItemRangeRemoved
:
/**
* @return True if updates should be processed.
*/
Boolean onItemRangeRemoved(int positionStart, int itemCount) {
if (itemCount < 1) {
return false;
}
// mPendingUpdates是List<UpdateOp>
// 這裏是將一個刪除的UpdateOp加入mPendingUpdates中
mPendingUpdates.add(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount, null));
mExistingUpdateTypes |= UpdateOp.REMOVE;
return mPendingUpdates.size() == 1;
}
複製代碼
AdapterHelper
對本身比較有信心,沒有交給別人處理,他在本身的mPendingUpdates
中加入一個刪除標記的UpdateOp
,這個mPendingUpdates
有什麼做用呢?咱們一樣在使用的時候介紹。回到RecyclerViewDataObserver
中的RecyclerViewDataObserver#notifyItemRemoved
,調用完AdapterHelper#onItemRangeRemoved
以後,它立馬又調用了requestLayout
進行界面刷新。
界面繪製一直是咱們以前博客的重點討論對象,本章咱們就數據通知再看一下關於數據通知的細節。
在RecyclerView#dispatchLayoutStep1
方法中,RecyclerView
會調用RecyclerView#processAdapterUpdatesAndSetAnimationFlags
處理Adapter
中的更新和爲動畫設置標記,這裏咱們只看適配器數據更新相關:
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設置了默認的mItemAnimator,
// 以及LinearLayout的supportsPredictiveItemAnimations()爲true
// 該方法返回爲true
return (mItemAnimator != null && mLayout.supportsPredictiveItemAnimations());
}
複製代碼
因爲RecyclerView#predictiveItemAnimationsEnabled
一般會返回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;
// 更新、移動標籤省略
}
}
mPendingUpdates.clear();
}
複製代碼
mPendingUpdates
是一個ArrayList<UpdateOp>
,上述方法就是消費咱們在以前添加進mPendingUpdates
的刪除UpdateOp
,在處理刪除屬性的UpdateOp
的AdapterHelper#applyRemove
方法中又調用AdapterHelper#postponeAndUpdateViewHolders
:
private void postponeAndUpdateViewHolders(UpdateOp op) {
mPostponedList.add(op);
switch (op.cmd) {
// ... 省略添加、更新、移動
case UpdateOp.REMOVE:
mCallback.offsetPositionsForRemovingLaidOutOrNewView(op.positionStart,op.itemCount);
break;
default:
throw new IllegalArgumentException("Unknown update op type for " + op);
}
}
複製代碼
真實的處理交給了AdapterHelper
中的mCallback
,而mCallback
的實現一樣也在RecyclerView
,那咱們直接查看mCallback
的具體實現:
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) {
// 跟新要刪除逇ViewHolder的位置信息
holder.flagRemovedAndOffsetPosition(positionStart - 1, -itemCount,
applyToPreLayout);
mState.mStructureChanged = true;
}
}
}
mRecycler.offsetPositionRecordsForRemove(positionStart, itemCount, applyToPreLayout);
requestLayout();
}
複製代碼
上述代碼的做用主要有兩點:
ViewHolder
加上刪除的flag
,更新ViewHolder
的位置ViewHolder
則更新位置在數據刪除之後,Adapter
的做用就是爲這些變化的ViewHolder
添加刪除標籤和更新位置信息,後續的處理就交給了LayoutManager
和ItemAnimator
,咱們在下面的動畫中分析~
好的動畫會讓界面的交互很天然,RecyclerView
做爲一款強大的UI控件,天然也是支持動畫的,沒錯,RecyclerView
子視圖動畫是由ItemAnimator
實現的。
上文中的Gif不適合講解,因而我換了一張聊天圖,一樣要刪除第一條信息:
Adapter
的結果是它更新了
ViewHolder的
一些
flag
,那麼這些有了
flag
的
ViewHolder
是如何處理的呢?
在此以前,簡單瞭解一下動畫相關類ViewInfoStore
:
預佈局是什麼呢?簡單來講,RecyclerView
進行真實的佈局以前,提早進行一次佈局,也就是說,LayoutManager#onLayoutChildren
方法會執行兩次,那麼爲何會執行兩次呢?咱們慢慢分析。
預佈局是一個很重要得過程,當有簡單的子視圖動畫發生的時候,它就會被觸發,這一點咱們得回顧一下RecyclerView#dispatchLayoutStep1
方法,直接進入其中的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會在第一次佈局完成之後設置爲true
mState.mRunSimpleAnimations = mFirstLayoutComplete
&& mItemAnimator != null
&& (mDataSetHasChangedAfterLayout
|| animationTypeSupported
|| mLayout.mRequestedSimpleAnimations)
&& (!mDataSetHasChangedAfterLayout
|| mAdapter.hasStableIds());
mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations
&& animationTypeSupported
&& !mDataSetHasChangedAfterLayout
&& predictiveItemAnimationsEnabled();
}
複製代碼
從上面的代碼中,咱們能夠看出:
RecyclerView
第一次佈局完成之後纔有資格觸發動畫,mFirstLayoutComplete
是在第一次佈局完成之後設置爲true
mState.mRunSimpleAnimations
爲true
是mState.mRunPredictiveAnimations
爲true
的充要條件,mState.mRunPredictiveAnimations
這個屬性很重要,由它決定是否進行預佈局從新回到RecyclerView#dispatchLayoutStep1
方法:
private void dispatchLayoutStep1() {
// ...
mViewInfoStore.clear();
processAdapterUpdatesAndSetAnimationFlags();
// 重置一些狀態
// ... 省略
mItemsAddedOrRemoved = mItemsChanged = false;
// 是否預佈局取決於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;
}
// 記錄當前的位置信息 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會layout每個子視圖,包括後面加入的子視圖和刪除的子視圖,這樣之後,layoutManager就很清楚
// 要執行哪些動畫了
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)) {
// 對於新出來的ViewHolder添加標籤
// ... 省略一些方法
mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
}
}
![](https://user-gold-cdn.xitu.io/2019/9/16/16d3a4d52bfc6558?w=1500&h=750&f=png&s=27308)
clearOldPositions();
} else {
clearOldPositions();
}
// ...
}
複製代碼
除了上面直接進入的方法,還有兩個if
語句。
這個內容很簡單,對預佈局前存在的ViewHolder
的的位置信息進行記錄。
第二個if語句的內容就複雜多了,首先會進行預佈局過程,該過程第一次調用了LayoutManager#onLayoutChildren
,關於佈局的具體過程,這裏我就不講解了,想要了解的同窗能夠翻閱我以前的文章:《抽絲剝繭RecyclerView - LayoutManager》。
須要指出的是,在添加子視圖中,調用了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
,它會爲ViewInfoStore
中ViewHolder
對應的記錄InfoRecord
添加已刪除的標記,在真實的佈局(非預佈局)中,被刪除的ViewHolder
是不會被使用的,因此說,只有預佈局纔會記錄刪除動畫。
預佈局完成,界面的樣子:
能夠看到,第一次佈局完了之後,須要刪除的ViewHolder
和自動填充的ViewHolder
都被加入了RecyclerView
,不過,RecyclerView#DispatchLayoutStep1
還沒結束,它會調用ViewInfoStore
會給新加入的ViewHolder
添加對應的InfoRecord
。
完成這個之後,RecyclerView
對於要處理哪些動畫就瞭如指掌了,這個也是預佈局的意義。
一樣不講具體的代碼,第二次佈局完成之後,界面變成了:
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())) {
// 在第一級緩存mAttachedScrap中,若是是刪除的ViewHolder
// 預佈局是可使用的,真實佈局不可使用
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
//...
return null;
}
複製代碼
雖然解決了當前的疑問,你可能還會有另一個疑問,**沒有了被刪除的子視圖,刪除動畫還怎麼執行呢?**咱們仍是先看看接下來的過程吧。
以前咱們記錄了那麼多ViewHolder
中子視圖的信息,如今到了使用的時候了:
private void dispatchLayoutStep3() {
// ...
if (mState.mRunSimpleAnimations) {
// Step 3: Find out where things are now, and process change animations.
// 找到當前的ViewHolder,執行須要執行的動畫
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
// 執行動畫
mViewInfoStore.process(mViewInfoProcessCallback);
}
// 重置一些跟動畫有關的類
// ...
mState.mRunSimpleAnimations = false;
mState.mRunPredictiveAnimations = false;
// ...
mViewInfoStore.clear();
}
複製代碼
這個函數的上半部分主要的目的是爲了給ViewInfoStore
裏的ViewHolder
相關的InfoRecord
添加Post
標籤,下半部分mViewInfoStore.process(mViewInfoProcessCallback)
則是咱們的核心功能 - 動畫執行,咱們重點看一下這個方法:
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);
// 根據不一樣的Flag執行不一樣的動畫
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);
}
}
// 回調接口
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
這個關鍵方法中,遍歷mLayoutHolderMap
獲取ViewHolder
綁定的InfoRecord
,根據不一樣flag
的InfoRecord
,回調不一樣的方法,進而處理不一樣的動畫,回調接口的實如今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) {
// 出現的動畫
animateAppearance(viewHolder, preInfo, info);
}
@Override
public void processPersistent(ViewHolder viewHolder,
@NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {
viewHolder.setIsRecyclable(false);
if (mDataSetHasChangedAfterLayout) {
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);
}
};
複製代碼
若是你注意到刪除方法,你的疑問就更大了,刪除的子視圖都沒了,還執行毛線刪除動畫?那我就得告訴你了:雖然當前RecyclerView
沒有須要刪除的子視圖,可是當前的ViewInfoStore
有ViewHolder
啊,因此在執行刪除動畫前會將ViewHolder
中的子視圖從新添加到RecyclerView
裏面,這裏看一下上面的processDisappeared
方法調用的RecyclerView#animateDisappearance
方法,來看看是否是這樣的:
void animateDisappearance(@NonNull ViewHolder holder,
@NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
// 將子視圖從新加入到界面
addAnimatingView(holder);
holder.setIsRecyclable(false);
// mItemAnimator執行刪除動畫
if (mItemAnimator.animateDisappearance(holder, preLayoutInfo, postLayoutInfo)) {
postAnimationRunner();
}
}
/**
* 將刪除的子視圖從新添加進界面
*/
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);
}
}
複製代碼
一圖瞭解當前界面ViewHolder的狀態和須要執行的動畫:
mItemAnimator
,其餘動畫也是如此。
ItemAnimator
是一個抽象類,因此一些方法須要具體的類實現,在沒有指定具體的ItemAnimator
狀況下,系統使用了默認的DefaultItemAnimator
。一圖簡單瞭解DefaultItemAnimator
機制:
DefaultItemAnimator
,你還能夠自定義一個
ItemAnimator
,主要實現增、刪、更新和移動等一些方法,本文就再也不深刻了,感興趣的同窗能夠自行研究。
在DefaultItemAnimator
的刪除動畫中,會對被刪除的子視圖執行透明度1-0的動畫,動畫結束後,會刪除子視圖和回收ViewHolder
,位移動畫沒有放在透明度動畫結束後調用,而是使用時間爲透明度動畫執行時間的延遲,因此看上去就像子視圖被刪除後下面的子視圖纔開始網上位移的。
動畫執行完畢之後,圖片就變成了:
以上就是RecyclerView
刪除部分的Adapter
和ItemAnimator
的調用原理,其餘方法同窗們能夠自行分析~
若是你想繼續瞭解RecyclcerView
:
第一篇:《抽絲剝繭RecyclerView - 化整爲零》
第二篇:《抽絲剝繭RecyclerView - LayoutManager》
特別分享篇: