熟悉RecyclerView
的同窗應該都知道,Adapter
做爲RecyclerView
四大組成部分(Adapter
,LayoutManager
,ItemAnimator
,ItemDecoration
)之一,其重要性天然是不言而喻。今天,咱們來分析一下Adapter
的源碼。我打算將Adapter
的源碼分析分爲兩個部分,一是,從普通的角度上來看Adapter
,從源碼的角度上來分析咱們平常使用的一些操做;二是,分析DiffUtil
,可能會涉及到Adapter
的部分源碼。因此Adapter
源碼分析分爲兩篇,本文是第一篇。數組
在分析Adapter
源碼以前,咱們先來回顧一下,咱們常用的幾個方法。緩存
方法名 | 做用 |
---|---|
onCreateViewHolder | 建立一個ViewHolder對象,主要做用是將數據保存在ViewHolder,以供後面bind操做使用 |
onBindViewHolder | 數據綁定方法 |
getItemCount | 當前Adapter擁有數據的數量,該方法必須被重寫,不然RecyclerView 展現不了任何數據 |
getItemViewType | 該方法帶一個Position,主要是返回當前位置的ViewType。這個方法一般用於一個RecyclerView 須要加載不一樣的佈局。 |
getItemId | 該方法表示的意思是返回當前位置Item的id,此方法只在setHasStableIds 設置爲true纔會生效 |
setHasStableIds | 設置當前RecyclerView 的ItemView 是否擁有固定id,跟getItemId 方法一塊兒使用。若是設置爲true,會提升RecyclerView 的緩存效率。 |
上表中所列的方法應該就是咱們使用Adapter
常用的方法,接下來,我將正式分析Adapter
的相關代碼。我打算從以下角度來分析:bash
- 從新從
RecyclerView
緩存角度來分析onCreateViewHolder
和onBindViewHolder
。onBindViewHolder
的一個重載方法--主要是用於局部刷新。- 結合
Adapter
,分析ViewHolder
的position。
onCreateViewHolder
方法和onBindViewHolder
方法算是咱們使用次數最多的方法,不少自定義Adapter
的框架也都是從這兩個方法入手的。咱們來看看這兩個方法到底有什麼做用。app
首先,咱們來看一下onCreateViewHolder
方法,從它的調用時機入手。框架
在本文以前,我分析過RecyclerView
的緩存機制,當時我將RecyclerView
的緩存分爲4級緩存,其中分別是:less
- 一級緩存:
scrap
數組- 二級緩存:
CachedView
- 三級緩存:
ViewCacheExtension
- 四級緩存:
RecyclerViewPool
LayoutManager
會獲取ViewHolder
時,若是4級緩存都沒有命中,就會調用Adapter
的onCreateViewHolder
方法來建立一個新的ViewHolder
。咱們來看看相關的代碼:ide
if (holder == null) {
long start = getNanoTime();
if (deadlineNs != FOREVER_NS
&& !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
// abort - we have a deadline we can't meet return null; } holder = mAdapter.createViewHolder(RecyclerView.this, type); if (ALLOW_THREAD_GAP_WORK) { // only bother finding nested RV if prefetching RecyclerView innerView = findNestedRecyclerView(holder.itemView); if (innerView != null) { holder.mNestedRecyclerView = new WeakReference<>(innerView); } } long end = getNanoTime(); mRecyclerPool.factorInCreateTime(type, end - start); if (DEBUG) { Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder"); } } 複製代碼
上面的代碼是RecyclerView
的tryGetViewHolderForPositionByDeadline
方法代碼片斷。以前,咱們在分析緩存機制時,就已經仔細分析這個方法,這裏我就再也不贅述,有興趣的同窗能夠我以前的文章:RecyclerView 源碼分析(三) - RecyclerView的緩存機制。源碼分析
咱們回到上面的代碼片斷中來,從上面的代碼上,咱們看到這裏是調用的是Adapter
的createViewHolder
方法來建立ViewHolder
。咱們來看看Adapter
的createViewHolder
方法:佈局
public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
try {
TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
final VH holder = onCreateViewHolder(parent, viewType);
if (holder.itemView.getParent() != null) {
throw new IllegalStateException("ViewHolder views must not be attached when"
+ " created. Ensure that you are not passing 'true' to the attachToRoot"
+ " parameter of LayoutInflater.inflate(..., boolean attachToRoot)");
}
holder.mItemViewType = viewType;
return holder;
} finally {
TraceCompat.endSection();
}
}
複製代碼
其實createViewHolder
方法裏面也沒有作什麼的操做,差很少就是調用onCreateViewHolder
方法。簡而言之,onCreateViewHolder
有點帶兜底的韻味,緩存都沒有命中,只能乖乖的建立ViewHolder
。post
咱們來看看第二方法,也就是onBindViewHolder
方法。
咱們都知道,onBindViewHolder
方法的做用是進行數據綁定,因此執行這個方法的條件相對於onCreateViewHolder
有點苛刻。爲何呢?咱們這麼想一下吧,假設咱們change了其中一個ItemView的數據,而後經過notifyItemChanged
來通知數據源已經改變。在這種狀況下,正常來講,都是隻刷新對應位置的ItemView就好了,不必刷新其餘數據沒有改變的ItemView(這裏的刷新就是指執行onBindViewHolder
方法)。如今,咱們來看看對應的執行代碼:
boolean bound = false;
if (mState.isPreLayout() && holder.isBound()) {
// do not update unless we absolutely have to.
holder.mPreLayoutPosition = position;
} else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
if (DEBUG && holder.isRemoved()) {
throw new IllegalStateException("Removed holder should be bound and it should"
+ " come here only in pre-layout. Holder: " + holder
+ exceptionLabel());
}
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
}
複製代碼
從上面的代碼,咱們能夠看出來,最後調用了tryBindViewHolderByDeadline
方法。而調用tryBindViewHolderByDeadline
方法條件比較苛刻,不過無論怎麼苛刻,只要記住一點,若是對應位置的數據被更新了,該位置會執行一次onBindViewHolder
方法。咱們繼續看一下tryBindViewHolderByDeadline
方法的代碼:
private boolean tryBindViewHolderByDeadline(ViewHolder holder, int offsetPosition,
int position, long deadlineNs) {
// ······
mAdapter.bindViewHolder(holder, offsetPosition);
// ······
}
複製代碼
執行過程跟onCreateViewHolder
方法差很少,都是在依靠Adapter
內部一個對應的final方法來回調。這樣所作的好處,能夠在onBindViewHolder
方法執行先後作一些其餘的操做,好比初始化操做和清理操做,這種模式有點相似於Java中靜態代理模式中的繼承代理。而後,咱們來看看Adapter
的bindViewHolder
方法:
public final void bindViewHolder(@NonNull VH holder, int position) {
holder.mPosition = position;
if (hasStableIds()) {
holder.mItemId = getItemId(position);
}
holder.setFlags(ViewHolder.FLAG_BOUND,
ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
holder.clearPayload();
final ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
if (layoutParams instanceof RecyclerView.LayoutParams) {
((LayoutParams) layoutParams).mInsetsDirty = true;
}
TraceCompat.endSection();
}
複製代碼
從這裏,咱們能夠簡單發現,在執行onBindViewHolder
方法先後,各自作了一些不一樣的操做。好比,在執行onBindViewHolder
方法以前,更新了ViewHolder
的mPosition
屬性和給ViewHolder
設置了一些flag;在執行onBindViewHolder
方法以後,清理了ViewHolder
的payload
,而且仍是給ItemView
的LayoutParams
的mInsetsDirty
屬性設置爲true。
這裏額外的提出兩個點:
payload
主要是用於局部刷新的,待會我會詳細解釋怎麼進行局部刷新。- 關於
LayoutParams
的mInsetsDirty
屬性,這個屬性尤其重要的,主要用於ItemView的繪製,後續我在分析ItemDecoration
時會詳細的解釋這個屬性。
在分析局部刷新以前,咱們先來討論一下怎麼進行佈局刷新,也就是說怎麼經過RecyclerView
實現ItemView
的局部刷新。
假設下面的一個Demo:
點擊一下下面灰色的Button
,position
爲0的ItemView
會改變顯示的文字。若是咱們不作局部刷新,出現什麼問題呢?咱們先來試試:
mDataList.get(0).setString("change data");
mAdapter.notifyItemChanged(0);
複製代碼
正常的實現應該就是上面的代碼,很是的簡單,也是咱們常常書寫的代碼。這樣書寫有什麼問題嗎?有很大的問題!就是整個ItemView會閃爍一下,效果以下:
網上給了一堆的緣由分析,我我的以爲,緣由很是的簡單,就是第一個ItemView執行的change動畫。因此介於這兩個緣由,咱們能夠找到兩種解決方案:
- 設置
RecyclerView
的change動畫時間爲0,也就是調用ItemAnimator
的setChangeDuration
方法。- 直接將
RecyclerView
的ItemAnimator
設置爲null。
對於第二種方案,我不置能否。這樣來想,咱們直接將動畫設置爲null,那麼RecyclerView
就沒有任何動畫,是否是感受有點得不償失?
第一種方案比起第二種方案稍微要好一些,咱們將change動畫時間設置爲0,隻影響了change動畫(至關於取消了change動畫),不會影響其餘其餘操做的動畫。不過,仍是感受美中不足,至關於後面全部的change操做都沒有了動畫,若是我想有些change操做有動畫呢?
此時就須要局部刷新出手了。咱們先來看看怎麼實現局部刷新:
首先,調用帶兩個參數的notifyItemChanged
方法,以下:
mAdapter.notifyItemChanged(0, "");
複製代碼
第二參數是一個payload,Object類型,因此咱們能夠傳遞任意對象,這裏就傳遞一個空字符串。
而後咱們得重寫Adapter
的onBindViewHolder
方法(這裏重寫的是帶三個參數的onBindViewHolder
方法,帶兩個參數的onBindViewHolder
該怎麼寫就怎麼寫)。
@Override
public void onBindViewHolder(@NonNull RecyclerViewHolder holder, int position, @NonNull List<Object> payloads) {
if (payloads.contains("")) {
holder.mTextView.setText(mDataList.get(position).getString());
} else {
super.onBindViewHolder(holder, position, payloads);
}
}
複製代碼
這裏咱們判斷了一下payloads
裏面是否含有以前咱們傳遞的空字符串,若是含有的話,直接更新顯示文字便可,若是不含有則走默認邏輯。如今來咱們看看效果:
局部刷新的使用是很是的簡單的,就是重寫了Adapter
帶三個參數的onBindViewHolder
方法,而後調用的也是帶兩個參數的notifyItemChanged
方法。
可是,咱們不由好奇,爲何這樣作ItemView就不會閃爍呢?我在這裏就能夠告訴你們答案,是由於沒有執行change動畫。爲了保持求知若飢,虛心若愚的良好傳統,你們確定會進一步的問,爲何在這種狀況下不會執行動畫呢?其實爲了回答這個問題,我早就已經爲你們打好鋪墊,在理解局部刷新的原理以前,你們最好已經理解了RecyclerView
的動畫機制,有興趣的同窗能夠看看我以前的文章:RecyclerView 源碼分析(四) - RecyclerView的動畫機制。
前面頻繁的源碼追蹤咱們這裏就不進行了,能夠參考個人文章:RecyclerView 源碼分析(四) - RecyclerView的動畫機制,這裏直接從根源入手。咱們都知道,當咱們調用Adapter
的notifyItemChanged
方法,會執行到AdapterHelper$Callback
的markViewHoldersUpdated
方法。
而咱們這裏不看markViewHoldersUpdated
方法,而是看哪裏調用了這個方法。
根據咱們辛苦的追蹤代碼,咱們發現主要是有兩個地方在調用markViewHoldersUpdated
方法:1. postponeAndUpdateViewHolders
方法;2. consumeUpdatesInOnePass
方法。
這其中,consumeUpdatesInOnePass
方法是咱們的老朋友,該方法主要是在dispatchLayoutStep2
方法,其做用也是不言而喻,主要是給消費以前添加的operation
。而postponeAndUpdateViewHolders
方法咱們就感受很是的陌生,這個方法是在哪裏被調用呢?
根據咱們的追蹤,發現它的調用源頭是AdapterHelper
的preProcess
方法。而preProcess
方法又是在哪裏被調用的呢?是在processAdapterUpdatesAndSetAnimationFlags
方法:
private void processAdapterUpdatesAndSetAnimationFlags() {
// ······
if (predictiveItemAnimationsEnabled()) {
mAdapterHelper.preProcess();
} else {
mAdapterHelper.consumeUpdatesInOnePass();
}
// ······
}
複製代碼
而processAdapterUpdatesAndSetAnimationFlags
方法只在dispatchLayoutStep1
方法調用(這裏不考慮非自動測量的狀況)。這裏,咱們就完全明瞭。dispatchLayoutStep1
方法階段被預佈局階段,也就是說,change操做在預佈局階段就已經回調markViewHoldersUpdated
方法。
而markViewHoldersUpdated
方法的做用是啥呢?其實在RecyclerView 源碼分析(四) - RecyclerView的動畫機制,我就已經解釋過了,主要做用有兩個:
- 給每一個
ViewHolder
打了對應的flag- 更新每一個
ViewHolder
的position。
關於這兩個做用的分析,flag咱們能夠直接跳過,position在後面我會詳細的分析。
從而,咱們知道,在預佈局階段,每一個ViewHolder
的position和flag就已經肯定了,這個有什麼做用呢?還記得咱們以前分析RecyclerView
的動畫機制說過,在預佈局階段若是條件容許的話,會進行一次佈局,也就是會調用LayoutManager
的onLayouyChildren
方法。
而onLayouyChildren
方法會作啥呢?我主要介紹兩點(這裏以LinearLayoutManager
爲例):
1.調用
LayoutManager
的detachAndScrapAttachedViews
方法,回收全部的ViewHolder,將他們放入四級緩存中。 2. 調用fill方法進行佈局。在fill方法調用流程會調用RecyclerView
的tryGetViewHolderForPositionByDeadline
方法從緩存中獲取ViewHolder
。
這裏咱們先來看回收部分,咱們知道detachAndScrapAttachedViews
方法最終會調用到Recycler
的scrapView
方法裏面去。咱們來看看scrapView
方法(請你們睜大眼睛,這是尋找答案的第一條線索):
void scrapView(View view) {
final ViewHolder holder = getChildViewHolderInt(view);
if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
|| !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
if (holder.isInvalid() && !holder.isRemoved() && !mAdapter.hasStableIds()) {
throw new IllegalArgumentException("Called scrap view with an invalid view."
+ " Invalid views cannot be reused from scrap, they should rebound from"
+ " recycler pool." + exceptionLabel());
}
holder.setScrapContainer(this, false);
mAttachedScrap.add(holder);
} else {
if (mChangedScrap == null) {
mChangedScrap = new ArrayList<ViewHolder>();
}
holder.setScrapContainer(this, true);
mChangedScrap.add(holder);
}
}
複製代碼
從這裏咱們知道,scrapView
方法的做用就是ViewHolder
分別放到mAttachedScrap
和mChanedScrap
數組。這裏咱們重點關注canReuseUpdatedViewHolder(holder)
這個判斷條件,咱們來追蹤這個方案的代碼,最終咱們找到了DefaultItemAnimator
的canReuseUpdatedViewHolder
方法:
@Override
public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder,
@NonNull List<Object> payloads) {
return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads);
}
複製代碼
看到沒?這裏判斷了一下payloads
是否爲空。這個有什麼做用呢?咱們回到scrapView
方法來,若是payloads
不爲空的話,當前的ViewHolder
會被回收到mAttachedScrap
。這裏,咱們必定要記得,當ViewHolder
的payloads
不爲空,那麼在回收時,ViewHolder
會被回收到mAttachedScrap
。這個有什麼做用呢?這就須要咱們去尋找第二條線索。
第二條線索就藏在tryGetViewHolderForPositionByDeadline
方法裏面。咱們來瞅瞅:
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 (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position
&& !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) {
holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP);
return holder;
}
}
// ······
}
複製代碼
結合上面的分析,當在預佈局階段,也就是dispatchLayoutStep1
階段進行佈局,經過帶兩個參數的notifyItemChanged
方法進行通知,確定會在上面的代碼返回一個ViewHolder
。也就是說,在這種狀況下,變化先後該ItemView
的ViewHolder
確定是同一個ViewHolder
。
如上就是第二條線索,那第二條線索有什麼做用呢?就得看第三條線索了。那第三條線索在哪裏呢?就在dispatchLayoutStep3
方法裏面。
咱們都知道,dispatchLayoutStep3
階段被稱爲後佈局,主要進行動畫的執行,咱們來看看咱們的change操做會執行哪些代碼:
private void dispatchLayoutStep3() {
// ······
if (mState.mRunSimpleAnimations) {
for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {
// ······
animateChange(oldChangeViewHolder, holder, preInfo, postInfo,
oldDisappearing, newDisappearing);
// ······
}
// ······
}
複製代碼
change操做確定會執行到如上的代碼,咱們在分析動畫機制時就已經分析過了。咱們來看看animateChange
方法:
private void animateChange(@NonNull ViewHolder oldHolder, @NonNull ViewHolder newHolder,
@NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo,
boolean oldHolderDisappearing, boolean newHolderDisappearing) {
// ······
if (oldHolder != newHolder) {
// ······
addAnimatingView(oldHolder);
// ······
}
// ······
}
複製代碼
看到沒,這就是最終的答案,只有兩個ViewHolder
不是同一個對象纔會添加一個AnimatingView
。
因爲局部刷新的先後,ItemView
的是同一個ViewHolder
對象,纔會致使局部刷新不會執行change動畫,纔會解決ItemView
的閃爍。
有可能有人又有疑問了,爲何會全局刷新不是同一個ViewHolder
呢?咱們經過scrapView
方法能夠知道,若是全局刷新,那麼change的ViewHolder
會被回收到mChangedScrap
數組裏面去,而在tryGetViewHolderForPositionByDeadline
方法裏面,咱們能夠知道,只有預佈局階段纔會從mChangedScrap
數組裏面獲取ViewHolder
對象:
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
複製代碼
因此預佈局階段和正式佈局階段同一個ItemView
確定是不一樣的ViewHolder
,從而會執行change動畫。
因爲這個問題的答案尋找起來比較麻煩,這裏我就針對這個問題作一個簡單的總結:
佈局刷新之因此能解決
ItemView
的閃爍問題,是由於在局部刷新的狀況下,不會執行change動畫。而不執行的chang動畫的緣由是由於在刷新先後都是同一個ViewHolder
,而且都是從mAttachedScrap
數組裏面得到,因此在動畫執行階段,不會執行局部刷新致使的change動畫,進而解決閃爍問題;而全局刷新因爲刷新先後不是同一個ViewHolder
,因此會執行change動畫。
在ViewHolder的內部有幾個讓人難以理解的問題,一個是flag,衆多的flag讓人很是的懵逼,這個我在緩存機制那一篇文章,我已經作了詳細的總結,有興趣的同窗能夠看看個人文章:RecyclerView 源碼分析(三) - RecyclerView的緩存機制;另外一個是position,本文來重點分析一下。
這裏主要分析兩個方法,分別是getAdapterPosition
和getLayoutPosition
,對應着ViewHolder
內部兩個成員變量mPosition
和mPreLayoutPosition
兩個屬性。
你們在使用這兩個方法時,應該都對這兩個方法有必定的疑問,這裏我簡單的解釋一下這兩個方法的區別(其實咱們從這兩個方法的註釋就能看出區別)。
咱們先來看看這兩個方法的代碼,首先來看一下getAdapterPosition
方法:
public final int getAdapterPosition() {
if (mOwnerRecyclerView == null) {
return NO_POSITION;
}
return mOwnerRecyclerView.getAdapterPositionFor(this);
}
複製代碼
別看getAdapterPosition
方法比較麻煩,還調用了RecyclerView
的getAdapterPositionFor
方法進行位置的計算。可是它表達的意思是很是簡單的,就是獲取當前ViewHolder所綁定ItemView的真實位置。這裏的真實位置說的比較籠統,這樣來解釋吧,當咱們remove掉爲position爲0的item,正常來講,後面ViewHolder
的position應該都減1。可是RecyclerView
處理Adapter
的更新採用的延遲處理策略,因此在正式處理以前獲取ViewHolder
的位置可能會出現偏差,介於這個緣由,getAdapterPosition
方法就出現了。
getAdapterPosition
方法是怎樣保證每次計算都是正確的呢?包括在正式處理以前呢?咱們知道,在RecyclerView
中,延遲處理的實現是在notify階段往一個叫mPendingUpdates
數組裏面添加Operation
,分別在dispatchLayoutStep1
階段或者dispatchLayoutStep2
階段進行處理。經過追蹤getAdapterPositionFor
方法,咱們知道getAdapterPosition
方法在計算位置時,考慮到mPendingUpdates
數組的存在,因此在notify階段和dispatchLayoutStep1
階段之間(這裏假設dispatchLayoutStep1
就會處理),getAdapterPosition
方法返回正確的位置。
而getLayoutPosition
方法呢?getLayoutPosition
方法就不能保證在notify階段和dispatchLayoutStep1
階段之間獲取的位置是正確的。爲何這麼說呢?咱們來看看getLayoutPosition
方法的代碼:
public final int getLayoutPosition() {
return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
}
複製代碼
getLayoutPosition
方法返回的是mPosition
或者mPreLayoutPosition
,可是在dispatchLayoutStep1
階段以前,還未更新每一個ViewHolder
的position,因此得到不必定的是正確(只有在處理mPendingUpdates
的操做時,position纔會被更新,對應着的代碼就是執行AdapterHelper$Callback
接口的方法)。
可是getLayoutPosition
方法爲何還有存在的必要呢?咱們發現getLayoutPosition
方法不會每次都計算,也就是說,getLayoutPosition
方法的效率比getAdapterPosition
方法高。當咱們在Adapter這種調用方法來獲取ViewHolder
的位置時,能夠優先考慮getLayoutPosition
方法,由於Adapter
的方法回調階段不在mPendingUpdates
處理以前,因此此時getLayoutPosition
方法跟getAdapterPosition
方法沒有任何區別了。
可是須要注意,若是咱們在其餘地方獲取ViewHolder
的position
,要特別注意這種狀況,由於其餘地方不能保證與RecyclerView
狀態同步,這種狀況爲了保證結果的正確性,咱們應該優先考慮getAdapterPosition
方法。
本文到這裏差很少就結束了,在這裏咱們作一個簡單的總結
- 之因此局部刷新能解決
ItemView
閃爍的問題,是由於局部刷新進行change操做時沒有執行change動畫。而沒有執行change動畫的緣由是由於在預佈局階段和後佈局階段,ItemView
的ViewHolder
是同一個對象。getAdapterPosition
方法在任什麼時候候獲取的都是ViewHolder
真實的位置,而getLayoutPosition
方法只在mPendingUpdates
數組處理以後才能獲取真實的位置。這是兩個方法區別。
下一篇文章是分析DiffUtil
的實現,來看看DiffUtil
來怎麼實現差量計算的。