概述
前面介紹過(guò)了临庇,內(nèi)存緩存主要是指在內(nèi)部存儲(chǔ)器存儲(chǔ)數(shù)據(jù)零截,可能大家聽得比較多的就是LruCache算法麸塞,里面會(huì)涉及到內(nèi)存緩存,下面以就以Android中比較常見的兩個(gè)控件涧衙,ListView/GridView跟RecyclerView來(lái)分析一下他們是如何通過(guò)緩存復(fù)用Item哪工,來(lái)展示大量數(shù)據(jù),由于ListView已經(jīng)有很多人分析過(guò)弧哎,其中郭霖早期寫了一篇文章Android ListView工作原理完全解析雁比,帶你從源碼的角度徹底理解,已經(jīng)分析的很到位了撤嫩,所以就不再說(shuō)ListView/GridView的繪制流程了偎捎,下面會(huì)重點(diǎn)分析一些ListView/GridView跟GridView的緩存原理,由于他們都是繼承自AbsListView序攘,所以緩存邏輯很自然地是在AbsListView中實(shí)現(xiàn)的鸭限,下面就來(lái)對(duì)一下AbsListView的緩存原理:
- AbsListView的緩存機(jī)制:如何復(fù)用Item
- RecyclerView的緩存機(jī)制:如何復(fù)用Item
- 定向刷新:RecyclerView如何定向刷新Item
- 局部刷新:RecyclerView如何實(shí)現(xiàn)局部刷新Item
- DiffUtil:如何找出新舊集合的差異
AbsListView
注釋
AbsListView是一個(gè)抽象類,官方注釋是這么寫的
Base class that can be used to implement virtualized lists of items. A list does not have a spatial definition here. For instance, subclases of this class can display the content of the list in a grid, in a carousel, as stack, etc.
用于展示大量數(shù)據(jù)的item的基類两踏。在這里并沒有指定List的展現(xiàn)方式败京。舉例來(lái)說(shuō),這個(gè)類的子類可以在網(wǎng)格梦染,列表中展示大量的數(shù)據(jù)
通過(guò)注釋可以很明顯的知道AbsListView作為GridView以及ListView的基類赡麦,沒有固定展示數(shù)據(jù)的形式朴皆,這個(gè)是交由他的子類來(lái)實(shí)現(xiàn)的,只是ListView是列表泛粹,GridView是網(wǎng)格遂铡,下面開始從源碼的角度來(lái)分析一下AbsListView的緩存機(jī)制。
繼承關(guān)系
GridView跟ListView并列晶姊,繼承自AbsListView扒接,然而AbsListView又是跟AdapterViewAnimator,AbsSpinner是同類的们衙,從這里可以看出钾怔,緩存的邏輯應(yīng)該為GridView跟ListView共有,所以應(yīng)該是在AbsListView中進(jìn)行整合的蒙挑,所以我們重點(diǎn)關(guān)注AbsListView這個(gè)類就可以了宗侦。AbsListView繼承自ViewGroup,那么很自然地就會(huì)進(jìn)行onMeasure忆蚀,onLayout方法矾利,下面就跟著這兩個(gè)方法來(lái)進(jìn)行 分析。
onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mSelector == null) {
useDefaultSelector();
}
//計(jì)算padding
final Rect listPadding = mListPadding;
listPadding.left = mSelectionLeftPadding + mPaddingLeft;
listPadding.top = mSelectionTopPadding + mPaddingTop;
listPadding.right = mSelectionRightPadding + mPaddingRight;
listPadding.bottom = mSelectionBottomPadding + mPaddingBottom;
// 如果ranscriptMode是TRANSCRIPT_MODE_NORMAL馋袜,
//當(dāng)Adapter中的數(shù)據(jù)集改變之后男旗,其子類會(huì)自動(dòng)滾動(dòng)到底部
if (mTranscriptMode == TRANSCRIPT_MODE_NORMAL) {
final int childCount = getChildCount();
final int listBottom = getHeight() - getPaddingBottom();
final View lastChild = getChildAt(childCount - 1);
final int lastBottom = lastChild != null ? lastChild.getBottom() : listBottom;
mForceTranscriptScroll = mFirstPosition + childCount >= mLastHandledItemCount &&
lastBottom <= listBottom;
}
}
onLayout
子類不能覆蓋onLayout方法,需要重寫layoutChildren方法
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
mInLayout = true;
final int childCount = getChildCount();
if (changed) {
for (int i = 0; i < childCount; i++) {
getChildAt(i).forceLayout();
}
//重新測(cè)量Child,mRecycler其實(shí)就是RecycleBin欣鳖,
//這個(gè)是一個(gè)用于管理回收的View的回收類,一會(huì)兒?jiǎn)为?dú)分析
mRecycler.markChildrenDirty();
}
//給Child布局察皇,一會(huì)兒會(huì)單獨(dú)分析
layoutChildren();
mInLayout = false;
mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;
// TODO: Move somewhere sane. This doesn't belong in onLayout().
if (mFastScroll != null) {
mFastScroll.onItemCountChanged(getChildCount(), mItemCount);
}
}
RecycleBin
在看RecyclerBin源碼之前,我們可以感性地分析一下观堂,一般的緩存分為初始化让网,存呀忧,取以及清空緩存师痕,實(shí)際上RecycleBin實(shí)際上也大體分這幾個(gè)步驟。
成員變量
private RecyclerListener mRecyclerListener;
//第一個(gè)可見的View存儲(chǔ)的位置
private int mFirstActivePosition;
//可見的View數(shù)組
private View[] mActiveViews = new View[0];
//不可見的的View數(shù)組,是一個(gè)集合數(shù)組而账,每一種type的item都有一個(gè)集合來(lái)緩存
private ArrayList<View>[] mScrapViews;
//View的Type的數(shù)量
private int mViewTypeCount;
//viewType為1的集合或者說(shuō)mScrapViews的第一個(gè)元素
private ArrayList<View> mCurrentScrap;
還有三個(gè)成員變量胰坟,沒有給出,因?yàn)樯婕暗?strong>StableId以及Transient State這兩種屬性泞辐,下面解釋一下
- StableId:就是所有的Item具有相同的ID笔横,也就是所有的Item都相同,通過(guò)復(fù)寫B(tài)aseAdapter中的hasStableIds可以進(jìn)行設(shè)置咐吼,默認(rèn)為false
- Transient State:在這里面有一個(gè)Transient State吹缔,是View的一個(gè)屬性,說(shuō)的是View伴隨有動(dòng)畫之類的效果锯茄,對(duì)于這種狀態(tài)的View只有跟Adapter綁定的數(shù)據(jù)源沒有發(fā)生變化或者View有相同的ID的時(shí)候才能進(jìn)行緩存復(fù)用厢塘,因?yàn)檫@兩種情況下Item要么數(shù)據(jù)不變茶没,不用重新綁定數(shù)據(jù),要么View不變晚碾,不需要重新創(chuàng)建
//數(shù)據(jù)集不變的具有TransientState的View數(shù)組
private SparseArray<View> mTransientStateViews;
//具有固定ID的具有TransientState的View數(shù)組
private LongSparseArray<View> mTransientStateViewsById;
//具有TransientState狀態(tài)但是不滿足以上任意一種狀態(tài)的View數(shù)組抓半,不予緩存
private ArrayList<View> mSkippedScrap;
初始化
setViewTypeCount
public void setViewTypeCount(int viewTypeCount) {
if (viewTypeCount < 1) {
throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
}
//根據(jù)viewTypeCount初始化數(shù)組
ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];
for (int i = 0; i < viewTypeCount; i++) {
scrapViews[i] = new ArrayList<View>();
}
//初始化RecycleBin的數(shù)組
mViewTypeCount = viewTypeCount;
mCurrentScrap = scrapViews[0];
mScrapViews = scrapViews;
存
fillActiveViews
存放屏幕上活躍的View數(shù)組
void fillActiveViews(int childCount, int firstActivePosition) {
//檢查ActiveView是否需要擴(kuò)容
if (mActiveViews.length < childCount) {
mActiveViews = new View[childCount];
}
mFirstActivePosition = firstActivePosition;
final View[] activeViews = mActiveViews;
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams();
// Don't put header or footer views into the scrap heap
if (lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
//對(duì)用非Header以及Footer的child給activeViews數(shù)組
activeViews[i] = child;
// 設(shè)置position的偏移量,方便接下來(lái)的布局
lp.scrappedFromPosition = firstActivePosition + i;
}
}
}
addScrapView
對(duì)不在屏幕中的View進(jìn)行緩存
void addScrapView(View scrap, int position) {
final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
if (lp == null) {
// Can't recycle, but we don't know anything about the view.
// Ignore it completely.
return;
}
//設(shè)置View的位置偏移量
lp.scrappedFromPosition = position;
final int viewType = lp.viewType;
//只要Type大于0就會(huì)進(jìn)行緩存
if (!shouldRecycleViewType(viewType)) {
//嘗試對(duì)非Header以及Footer的View進(jìn)行緩存
if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
getSkippedScrap().add(scrap);
}
return;
}
scrap.dispatchStartTemporaryDetach();
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
// 不直接緩存具有transient state的View格嘁,用transient數(shù)組進(jìn)行緩存
final boolean scrapHasTransientState = scrap.hasTransientState();
if (scrapHasTransientState) {
if (mAdapter != null && mAdapterHasStableIds) {
//具有stableId
if (mTransientStateViewsById == null) {
mTransientStateViewsById = new LongSparseArray<>();
}
//存放進(jìn)mTransientStateViewsById數(shù)組
mTransientStateViewsById.put(lp.itemId, scrap);
} else if (!mDataChanged) {
//數(shù)據(jù)集合沒有改變笛求,暫時(shí)沒想到使用場(chǎng)景
if (mTransientStateViews == null) {
mTransientStateViews = new SparseArray<>();
}
//存放進(jìn)mTransientStateViews數(shù)組
mTransientStateViews.put(position, scrap);
} else {
// 除此之外,不予緩存糕簿,放入mSkippedScrap過(guò)濾數(shù)組
getSkippedScrap().add(scrap);
}
} else {
//非transient state數(shù)組探入,
if (mViewTypeCount == 1) {
//只有一種type,直接添加進(jìn)mCurrentScrap
mCurrentScrap.add(scrap);
} else {
//多type冶伞,則存放進(jìn)對(duì)應(yīng)type的數(shù)組
mScrapViews[viewType].add(scrap);
}
//回調(diào)函數(shù)新症,通知View已經(jīng)移動(dòng)到Scrap進(jìn)行緩存
if (mRecyclerListener != null) {
mRecyclerListener.onMovedToScrapHeap(scrap);
}
}
}
reclaimViews
開辟新集合,存儲(chǔ)所有的View包含ActivieView以及ScrapView
public void reclaimViews(List<View> views) {
int childCount = getChildCount();
RecyclerListener listener = mRecycler.mRecyclerListener;
// Reclaim views on screen
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams();
// Don't reclaim header or footer views, or views that should be ignored
if (lp != null && mRecycler.shouldRecycleViewType(lp.viewType)) {
views.add(child);
child.setAccessibilityDelegate(null);
if (listener != null) {
// Pretend they went through the scrap heap
listener.onMovedToScrapHeap(child);
}
}
}
//添加緩存的View
mRecycler.reclaimScrapViews(views);
removeAllViewsInLayout();
}
取
getScrapView
獲取緩存的View
View getScrapView(int position) {
//拿到當(dāng)前位置View的type
final int whichScrap = mAdapter.getItemViewType(position);
if (whichScrap < 0) {
return null;
}
if (mViewTypeCount == 1) {
//type的種類為1响禽,直接從第一個(gè)數(shù)組中取
return retrieveFromScrap(mCurrentScrap, position);
} else if (whichScrap < mScrapViews.length) {
//直接從對(duì)應(yīng)的type數(shù)組中取
return retrieveFromScrap(mScrapViews[whichScrap], position);
}
return null;
}
getActiveView
根據(jù)位置獲取屏幕中顯示的某一個(gè)數(shù)組
View getActiveView(int position) {
int index = position - mFirstActivePosition;
final View[] activeViews = mActiveViews;
if (index >=0 && index < activeViews.length) {
final View match = activeViews[index];
//獲取之后將數(shù)組置空徒爹,便于虛擬機(jī)回收
activeViews[index] = null;
return match;
}
return null;
}
getTransientStateView
根據(jù)position獲取TransientStateView,獲取后會(huì)移除相應(yīng)的View
View getTransientStateView(int position) {
if (mAdapter != null && mAdapterHasStableIds && mTransientStateViewsById != null) {
long id = mAdapter.getItemId(position);
View result = mTransientStateViewsById.get(id);
mTransientStateViewsById.remove(id);
return result;
}
if (mTransientStateViews != null) {
final int index = mTransientStateViews.indexOfKey(position);
if (index >= 0) {
View result = mTransientStateViews.valueAt(index);
mTransientStateViews.removeAt(index);
return result;
}
}
return null;
}
getSkippedScrap
獲取過(guò)濾掉也是不緩存的數(shù)組
private ArrayList<View> getSkippedScrap() {
if (mSkippedScrap == null) {
mSkippedScrap = new ArrayList<>();
}
return mSkippedScrap;
}
retrieveFromScrap
從一個(gè)type對(duì)應(yīng)的緩存集合中尋找指定位置的View
private View retrieveFromScrap(ArrayList<View> scrapViews, int position) {
final int size = scrapViews.size();
if (size > 0) {
// See if we still have a view for this position or ID.
for (int i = 0; i < size; i++) {
final View view = scrapViews.get(i);
final AbsListView.LayoutParams params =
(AbsListView.LayoutParams) view.getLayoutParams();
if (mAdapterHasStableIds) {
final long id = mAdapter.getItemId(position);
if (id == params.itemId) {
return scrapViews.remove(i);
}
} else if (params.scrappedFromPosition == position) {
final View scrap = scrapViews.remove(i);
clearAccessibilityFromScrap(scrap);
return scrap;
}
}
final View scrap = scrapViews.remove(size - 1);
clearAccessibilityFromScrap(scrap);
return scrap;
} else {
return null;
}
}
清除
clearTransientStateViews
清空TransientStateViews
void clearTransientStateViews() {
final SparseArray<View> viewsByPos = mTransientStateViews;
if (viewsByPos != null) {
final int N = viewsByPos.size();
for (int i = 0; i < N; i++) {
removeDetachedView(viewsByPos.valueAt(i), false);
}
viewsByPos.clear();
}
final LongSparseArray<View> viewsById = mTransientStateViewsById;
if (viewsById != null) {
final int N = viewsById.size();
for (int i = 0; i < N; i++) {
removeDetachedView(viewsById.valueAt(i), false);
}
viewsById.clear();
}
}
removeSkippedScrap
刪除過(guò)濾掉的數(shù)組
void removeSkippedScrap() {
if (mSkippedScrap == null) {
return;
}
final int count = mSkippedScrap.size();
for (int i = 0; i < count; i++) {
removeDetachedView(mSkippedScrap.get(i), false);
}
mSkippedScrap.clear();
}
clearScrap
清空指定的Scrap集合
private void clearScrap(final ArrayList<View> scrap) {
final int scrapCount = scrap.size();
for (int j = 0; j < scrapCount; j++) {
removeDetachedView(scrap.remove(scrapCount - 1 - j), false);
}
}
clear
清空所有緩存數(shù)組
void clear() {
if (mViewTypeCount == 1) {
final ArrayList<View> scrap = mCurrentScrap;
clearScrap(scrap);
} else {
final int typeCount = mViewTypeCount;
for (int i = 0; i < typeCount; i++) {
final ArrayList<View> scrap = mScrapViews[i];
clearScrap(scrap);
}
}
clearTransientStateViews();
}
pruneScrapViews
當(dāng)緩存的數(shù)組大于Activite數(shù)組之后就需要進(jìn)行清理了芋类,主要是因?yàn)閠ransient state View數(shù)組中View的transient屬性已經(jīng)消失了隆嗅,所以相當(dāng)于會(huì)被緩存兩邊,一遍是具有transient state的View侯繁,一邊是這些View不再具有transient state的時(shí)候又會(huì)進(jìn)行緩存一次胖喳,這樣就會(huì)導(dǎo)致緩存中的數(shù)組大于Active數(shù)組,所以進(jìn)行處理贮竟。
private void pruneScrapViews() {
final int maxViews = mActiveViews.length;
final int viewTypeCount = mViewTypeCount;
final ArrayList<View>[] scrapViews = mScrapViews;
//Scrap數(shù)組遍歷
for (int i = 0; i < viewTypeCount; ++i) {
final ArrayList<View> scrapPile = scrapViews[i];
int size = scrapPile.size();
while (size > maxViews) {
scrapPile.remove(--size);
}
}
//transViewsByPos遍歷
final SparseArray<View> transViewsByPos = mTransientStateViews;
if (transViewsByPos != null) {
for (int i = 0; i < transViewsByPos.size(); i++) {
final View v = transViewsByPos.valueAt(i);
if (!v.hasTransientState()) {
removeDetachedView(v, false);
transViewsByPos.removeAt(i);
i--;
}
}
}
//mTransientStateViewsById遍歷
final LongSparseArray<View> transViewsById = mTransientStateViewsById;
if (transViewsById != null) {
for (int i = 0; i < transViewsById.size(); i++) {
final View v = transViewsById.valueAt(i);
if (!v.hasTransientState()) {
removeDetachedView(v, false);
transViewsById.removeAt(i);
i--;
}
}
}
}
markChildrenDirty
此方法會(huì)重新調(diào)用緩存的child的forcelayout,forcelayout跟requestLayout的區(qū)別在于前者只會(huì)重新執(zhí)行自己的onMeasure跟onLayout丽焊,后者不僅僅會(huì)執(zhí)行前者,還會(huì)調(diào)用parentView的requestLayout咕别,此方法會(huì)在ListView的size改變的時(shí)候進(jìn)行調(diào)用技健。
public void markChildrenDirty() {
if (mViewTypeCount == 1) {
final ArrayList<View> scrap = mCurrentScrap;
final int scrapCount = scrap.size();
for (int i = 0; i < scrapCount; i++) {
scrap.get(i).forceLayout();
}
} else {
final int typeCount = mViewTypeCount;
for (int i = 0; i < typeCount; i++) {
final ArrayList<View> scrap = mScrapViews[i];
final int scrapCount = scrap.size();
for (int j = 0; j < scrapCount; j++) {
scrap.get(j).forceLayout();
}
}
}
if (mTransientStateViews != null) {
final int count = mTransientStateViews.size();
for (int i = 0; i < count; i++) {
mTransientStateViews.valueAt(i).forceLayout();
}
}
if (mTransientStateViewsById != null) {
final int count = mTransientStateViewsById.size();
for (int i = 0; i < count; i++) {
mTransientStateViewsById.valueAt(i).forceLayout();
}
}
}
?
上面詳細(xì)分了RecyclerBin的成員變量以及生命周期,內(nèi)部通過(guò)定義了多個(gè)數(shù)組來(lái)對(duì)不同類型的View進(jìn)行緩存惰拱,那么AbsListView的子類也就是ListView以及GridView滾動(dòng)雌贱,以及調(diào)用notifyDataSetChanged的時(shí)候,然后進(jìn)行重新繪制偿短,先從緩存中取如果沒有取到則會(huì)重新創(chuàng)建一個(gè)View欣孤,關(guān)于這方面的分析網(wǎng)上已經(jīng)有很多文章了,只是很多文章沒有對(duì)RecycleBin的TranslateStateView數(shù)組進(jìn)行分析昔逗,自己之前也是一直沒有完全理解降传,所以重點(diǎn)分析了RecycleBin這個(gè)類,至于ListView以及GridView的繪制以及刷新機(jī)制勾怒,其實(shí)相對(duì)于RecyclerView來(lái)講其實(shí)是比較簡(jiǎn)單的婆排,所以大部分精力也將用來(lái)分析RecyclerView的緩存機(jī)制
RecyclerView
注釋
A flexible view for providing a limited window into a large data set
能夠在有限的窗口內(nèi)展示大量數(shù)據(jù)集合的一個(gè)靈活的View款票。
相對(duì)于AbsListView的兩個(gè)子類ListView以及GridView來(lái)講,RecyclerView最大一個(gè)特性就是靈活泽论,主要體現(xiàn)在以下兩個(gè)方面
- 多樣式:可以對(duì)數(shù)據(jù)的展示進(jìn)行自有定制艾少,可以是列表,網(wǎng)格甚至是瀑布流翼悴,除此之外你還可以自定義樣式
- 定向刷新:可以對(duì)指定的Item數(shù)據(jù)進(jìn)行刷新
- 刷新動(dòng)畫:RecyclerView支持對(duì)Item的刷新添加動(dòng)畫
- 添加裝飾:相對(duì)于ListView以及GridView的單一的分割線缚够,RecyclerView可以自定義添加分割樣式
今天我們的重點(diǎn)在于緩存,所以重點(diǎn)研究定向刷新鹦赎,除了對(duì)整體數(shù)據(jù)進(jìn)行刷新之外谍椅,RecyclerView還提供了很多定向刷新的方法。
由于RecyclerView的很多核心功能都是通過(guò)內(nèi)部類來(lái)實(shí)現(xiàn)的古话,而且作者又是把內(nèi)部類寫在一個(gè)類里面雏吭,所以還是要先對(duì)每個(gè)類的功能進(jìn)行簡(jiǎn)單介紹一下,整個(gè)RecyclerView的源代碼有1萬(wàn)多行陪踩,不可能也沒必要面面俱到杖们,還是有針對(duì)性地進(jìn)行分析,不然看源碼絕對(duì)會(huì)走火入魔肩狂。
內(nèi)部類 | |
---|---|
RecyclerView.LayoutManager | 負(fù)責(zé)Item視圖的布局的顯示管理 |
RecyclerView.ItemDecoration | 給每一項(xiàng)Item視圖添加修飾的View, |
RecyclerView.Adapter | 為每一項(xiàng)Item創(chuàng)建視圖 |
RecyclerView.ViewHolder | 承載Item視圖的子布局 |
RecyclerView.ItemAnimator | 負(fù)責(zé)處理數(shù)據(jù)添加或者刪除時(shí)候的動(dòng)畫效果 |
RecyclerView.Cache | Recycler/RecycledViewPool/ViewCacheExtension |
他們的作用如下表
內(nèi)部類 | |
---|---|
RecyclerView.LayoutManager | 負(fù)責(zé)Item視圖的布局的顯示管理 |
RecyclerView.ItemDecoration | 給每一項(xiàng)Item視圖添加修飾的View, |
RecyclerView.Adapter | 為每一項(xiàng)Item創(chuàng)建視圖 |
RecyclerView.ViewHolder | 承載Item視圖的子布局 |
RecyclerView.ItemAnimator | 負(fù)責(zé)處理數(shù)據(jù)添加或者刪除時(shí)候的動(dòng)畫效果 |
RecyclerView.Cache | Recycler/RecycledViewPool/ViewCacheExtension |
最后一個(gè)Cache是我加上去的摘完,實(shí)際上并沒有這個(gè)類,主要是為了說(shuō)明RecyclerView對(duì)Item強(qiáng)大的緩存傻谁,也是接下來(lái)重點(diǎn)分析的對(duì)象
Observer&Observable
Observer
Recycler沒有采用系統(tǒng)的Observer孝治,因?yàn)樽约盒枰邮胀ㄖ姆椒ㄟ^(guò)多,所以自己設(shè)計(jì)了一個(gè)觀察者AdapterDataObserver审磁,先看一下繼承關(guān)系
AdapterDataObserver
public static abstract class AdapterDataObserver {
public void onChanged() {
// Do nothing
}
public void onItemRangeChanged(int positionStart, int itemCount) {
// do nothing
}
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
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
private class RecyclerViewDataObserver extends AdapterDataObserver {
RecyclerViewDataObserver() {
}
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
mState.mStructureChanged = true;
setDataSetChangedAfterLayout();
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
triggerUpdateProcessor();
}
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {
triggerUpdateProcessor();
}
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {
triggerUpdateProcessor();
}
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {
triggerUpdateProcessor();
}
}
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
}
這里面在notify的方法里面谈飒,提到了一個(gè)類mAdapterHelper,顧名思義是Adapter的幫助類态蒂,他是AdapterHelper的實(shí)例杭措,用來(lái)幫助Adapter進(jìn)行數(shù)據(jù)更新,除此之外還有幾個(gè)類也是相對(duì)比較重要的,下面會(huì)簡(jiǎn)單介紹一下吃媒,因?yàn)镽ecyclerView的源碼有1萬(wàn)多行瓤介,作者幾乎是把所有與RecyclerView相關(guān)的類搞成了內(nèi)部類吕喘,可能作者對(duì)內(nèi)部類有一種特殊的感情赘那,問(wèn)題是你可以這樣子搞,但是代碼能不能多加點(diǎn)注釋氯质,很多方法完全沒注釋募舟,看起來(lái)真心一臉懵逼的類。
Observable
RecyclerView實(shí)際采用了系統(tǒng)的Observable闻察,看一下繼承關(guān)系
Observable
代碼比較簡(jiǎn)單拱礁,不忍注釋
public abstract class Observable<T> {
//注冊(cè)的觀察者
protected final ArrayList<T> mObservers = new ArrayList<T>();
//注冊(cè)單個(gè)觀察者
public void registerObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
if (mObservers.contains(observer)) {
throw new IllegalStateException("Observer " + observer + " is already registered.");
}
mObservers.add(observer);
}
}
//解綁單個(gè)觀察者
public void unregisterObserver(T observer) {
if (observer == null) {
throw new IllegalArgumentException("The observer is null.");
}
synchronized(mObservers) {
int index = mObservers.indexOf(observer);
if (index == -1) {
throw new IllegalStateException("Observer " + observer + " was not registered.");
}
mObservers.remove(index);
}
}
//移除所有的觀察者
public void unregisterAll() {
synchronized(mObservers) {
mObservers.clear();
}
}
}
AdapterDataObservable
比較簡(jiǎn)單琢锋,懶得注釋,寫出來(lái)是便于梳理整個(gè)流程
static class AdapterDataObservable extends Observable<AdapterDataObserver> {
public boolean hasObservers() {
return !mObservers.isEmpty();
}
public void notifyChanged() {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
public void notifyItemRangeChanged(int positionStart, int itemCount) {
notifyItemRangeChanged(positionStart, itemCount, null);
}
public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
}
}
public void notifyItemRangeInserted(int positionStart, int itemCount) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
}
}
public void notifyItemRangeRemoved(int positionStart, int itemCount) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeRemoved(positionStart, itemCount);
}
}
public void notifyItemMoved(int fromPosition, int toPosition) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeMoved(fromPosition, toPosition, 1);
}
}
}
看完了RecyclerView的觀察者呢灶,感覺實(shí)際上根本不需要繼承系統(tǒng)的被觀察者以及自定義觀察者吴超,因?yàn)橹挥幸粋€(gè)觀察者,所以干脆使用接口回調(diào)鸯乃,口味更佳鲸阻,下面繼續(xù)分析RecyclerView緩存的核心類。
Recycler
在Recycler中實(shí)際上緩存VieHolder的有2類集合缨睡,一類是可見的ViewHolder數(shù)組鸟悴,一類是不可見的ViewHolder數(shù)組咪橙,其中可見的數(shù)組中又分為數(shù)據(jù)改變跟沒有改變的盆繁,理解了這些,其實(shí)去看
注釋
A Recycler is responsible for managing scrapped or detached item views for reuse.
A "scrapped" view is a view that is still attached to its parent RecyclerView but
that has been marked for removal or reuse.
Recycler是用于管理廢棄的item或者從RecyclerView中移除的View便于復(fù)用怕午,廢棄的View是指仍然依附在RecyclerView中陋守,但是已經(jīng)被標(biāo)記為移除的或者可以復(fù)用的震贵。
跟ListView的RecycleBin一樣,Recycler也是RecyclerView設(shè)計(jì)的一個(gè)專門用于回收ViewHolder的類水评,其實(shí)RecyclerView的緩存機(jī)制是在ListView的緩存機(jī)制的基礎(chǔ)上進(jìn)一步的完善屏歹,所以在Recycler中能看到很多跟RecycleBin一樣的設(shè)計(jì)思想,在緩存這個(gè)層面上之碗,RecyclerView實(shí)際上并沒有做出太大的創(chuàng)新蝙眶,最大的創(chuàng)新來(lái)源于給每一個(gè)ViewHolder增加了一個(gè)UpdateOp,通過(guò)這個(gè)標(biāo)志可以進(jìn)行定向刷新指定的Item褪那,并且通過(guò)Payload參數(shù)可以對(duì)Item進(jìn)行局部刷新幽纷,我覺得這個(gè)是RecyclerView最厲害的地方,大大提高了刷新時(shí)候的性能博敬,如果數(shù)據(jù)源需要經(jīng)常變動(dòng)友浸,那么RecyclerView是你最好的選擇,沒有之一偏窝,下面看一下Recycler是如何進(jìn)行緩存的收恢。
成員變量
static final int DEFAULT_CACHE_SIZE = 2;//默認(rèn)緩存的數(shù)量
private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;//設(shè)置的緩存最大數(shù)量,默認(rèn)為2
int mViewCacheMax = DEFAULT_CACHE_SIZE;//View的緩存的最大數(shù)量祭往,默認(rèn)為2
RecycledViewPool mRecyclerPool;//RecycledView伦意,用來(lái)公用RecyclerView
//緩存的擴(kuò)展,可以用來(lái)對(duì)指定的position跟Type進(jìn)行緩存
private ViewCacheExtension mViewCacheExtension;
ArrayList<ViewHolder> mChangedScrap = null;//數(shù)據(jù)源更改過(guò)的AttachedScrap
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();//依附的緩存View
//緩存的全部View硼补,包含可見跟不可見的ViewHolder
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
private final List<ViewHolder> mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);//mAttachedScrap的不可變集合
初始化
我們發(fā)現(xiàn)在成員變量中沒有初始化的只有兩個(gè)變量mChangedScrap驮肉,mRecyclerPool,mChangedScrap是可見的數(shù)據(jù)源改變的ViewHolder集合已骇,mRecyclerPool是RecycledViewPool的集合离钝,用來(lái)緩存RecyclerView的集合票编,可以實(shí)現(xiàn)RecyclerView的復(fù)用。
mChangedScrap是在scrapView這個(gè)方法進(jìn)行初始化的
if (mChangedScrap == null) {
mChangedScrap = new ArrayList<ViewHolder>();
}
RecycledViewPool初始化
void setRecycledViewPool(RecycledViewPool pool) {
if (mRecyclerPool != null) {
mRecyclerPool.detach();
}
mRecyclerPool = pool;
if (pool != null) {
mRecyclerPool.attach(getAdapter());
}
}
存
recycleView
回收不可見的View卵渴,對(duì)于某些特定的View會(huì)放進(jìn)RecyclerViewPool慧域,如果這個(gè)ViewHolder是從緩存中取的,那么就會(huì)清空緩存中View
public void recycleView(View view) {
//這邊傳遞過(guò)來(lái)的是一個(gè)View浪读,然后通過(guò)View獲取ViewHolder
ViewHolder holder = getChildViewHolderInt(view);
//如果Holder被已經(jīng)被打上了移除的標(biāo)記吊趾,那么就從移除此View
if (holder.isTmpDetached()) {
removeDetachedView(view, false);
}
if (holder.isScrap()) {
//如果此Holder是來(lái)自緩存的可見的ViewHolder數(shù)組,清楚緩存
holder.unScrap();
} else if (holder.wasReturnedFromScrap()) {
//如果此Holder是來(lái)自緩存的不可見的ViewHolder數(shù)組瑟啃,清除緩存
holder.clearReturnedFromScrapFlag();
}
//開始緩存论泛,繼續(xù)追源碼
recycleViewHolderInternal(holder);
}
recycleViewHolderInternal
void recycleViewHolderInternal(ViewHolder holder) {
//此處省略若干行條件判斷代碼
if (forceRecycle || holder.isRecyclable()) {
//如果緩存數(shù)量大于0,并且ViewHolder的Flag標(biāo)志是有效的
//且非REMOVED跟UPDATE蛹屿,進(jìn)行緩存屁奏,
if (mViewCacheMax > 0&& !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_REMOVED| ViewHolder.FLAG_UPDATE
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
int cachedViewSize = mCachedViews.size();
if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
// 移除cachedViews中的第一個(gè)View,也就是第一個(gè)
recycleCachedViewAt(0);
cachedViewSize--;
}
//獲取添加元素的在緩存集合中的下標(biāo)
int targetCacheIndex = cachedViewSize;
if (ALLOW_THREAD_GAP_WORK&& cachedViewSize > 0
&& !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
int cacheIndex = cachedViewSize - 1;
while (cacheIndex >= 0) {
int cachedPos = mCachedViews.get(cacheIndex).mPosition;
//緩存的時(shí)候不能覆蓋最近經(jīng)常被使用到緩存
if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
break;
}
cacheIndex--;
}
targetCacheIndex = cacheIndex + 1;
}
//添加緩存
mCachedViews.add(targetCacheIndex, holder);
cached = true;
}
//如果沒有緩存的話就添加進(jìn)RecycledViewPool
if (!cached) {
addViewHolderToRecycledViewPool(holder, true);
recycled = true;
}
} else {
if (DEBUG) {
Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will "
+ "re-visit here. We are still removing it from animation lists"
+ exceptionLabel());
}
}
//mViewInfoStore中移除這條記錄
mViewInfoStore.removeViewHolder(holder);
if (!cached && !recycled && transientStatePreventsRecycling) {
holder.mOwnerRecyclerView = null;
}
}
Recycler的緩存做了很多優(yōu)化错负,實(shí)際上采用了LFU算法坟瓢,也就是最少使用策略,當(dāng)我們存儲(chǔ)ViewHolder的時(shí)候犹撒,會(huì)去判斷這個(gè)ViewHolder是否是來(lái)自緩存折联,如果是的話,那么在此緩存的時(shí)候不能覆蓋最近使用比較頻繁的緩存识颊,而是自己定義了一個(gè)類GapWorker,他有一個(gè)內(nèi)部類LayoutPrefetchRegistryImpl诚镰,然后定義了一個(gè)數(shù)組
int[] mPrefetchArray;
這個(gè)數(shù)組是用來(lái)記錄最近使用過(guò)的緩存Holder,所以我們?cè)诖娴臅r(shí)候會(huì)跟這里面存儲(chǔ)過(guò)的ViewHolder進(jìn)行匹配祥款,上面代碼中已經(jīng)進(jìn)行了注釋清笨,比較好理解。
recycleAndClearCachedViews
將CacheViews中的ViewHolder添加進(jìn)RecyclerViewHolder刃跛,然后清空CacheViews
void recycleAndClearCachedViews() {
final int count = mCachedViews.size();
for (int i = count - 1; i >= 0; i--) {
//將CacheView中的添加進(jìn)RecyclerViewPool
recycleCachedViewAt(i);
}
//清空集合
mCachedViews.clear();
if (ALLOW_THREAD_GAP_WORK) {
mPrefetchRegistry.clearPrefetchPositions();
}
}
recycleCachedViewAt
void recycleCachedViewAt(int cachedViewIndex) {
//省略一下判斷代碼
ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
//添加進(jìn)ToRecycledViewPool
addViewHolderToRecycledViewPool(viewHolder, true);
//從mCachedViews移除ViewHolder
mCachedViews.remove(cachedViewIndex);
}
addViewHolderToRecycledViewPool
void addViewHolderToRecycledViewPool(ViewHolder holder, boolean dispatchRecycled) {
clearNestedRecyclerViewIfNotNested(holder);
if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE)) {
holder.setFlags(0, ViewHolder.FLAG_SET_A11Y_ITEM_DELEGATE);
ViewCompat.setAccessibilityDelegate(holder.itemView, null);
}
if (dispatchRecycled) {
dispatchViewRecycled(holder);
}
holder.mOwnerRecyclerView = null;
//放入RecyclerViewPool
getRecycledViewPool().putRecycledView(holder);
}
取
取緩存有很多入口抠艾,但是最終都是調(diào)用了tryGetViewHolderForPositionByDeadline,下面重點(diǎn)分析一下此方法
tryGetViewHolderForPositionByDeadline
/**
* @param position Position of ViewHolder to be returned.
* @param dryRun True if the ViewHolder should not be removed from scrap/cache/
* @param deadlineNs Time, relative to getNanoTime(), by which bind/create work should complete. If FOREVER_NS is passed, this method will not fail to
create/bind the holder if needed.
*/
@Nullable
ViewHolder tryGetViewHolderForPositionByDeadline(
int position,boolean dryRun, long deadlineNs) {
boolean fromScrapOrHiddenOrCache = false;
ViewHolder holder = null;
// 0) 首先從Attached中的Changed數(shù)組中取
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
// 1) 分別從AttachedScrap,Hidden桨昙,Cached中獲取ViewHolder
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
if (holder != null) {
if (!validateViewHolderForOffsetPosition(holder)) {
if (!dryRun) {
holder.addFlags(ViewHolder.FLAG_INVALID);
if (holder.isScrap()) {
removeDetachedView(holder.itemView, false);
holder.unScrap();
} else if (holder.wasReturnedFromScrap()) {
holder.clearReturnedFromScrapFlag();
}
recycleViewHolderInternal(holder);
}
holder = null;
} else {
fromScrapOrHiddenOrCache = true;
}
}
}
if (holder == null) {
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
final int type = mAdapter.getItemViewType(offsetPosition);
// 2)通過(guò)StableId進(jìn)行獲取检号,針對(duì)復(fù)寫了BaseAdapter的StableId
if (mAdapter.hasStableIds()) {
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
type, dryRun);
if (holder != null) {
// update position
holder.mPosition = offsetPosition;
fromScrapOrHiddenOrCache = true;
}
}
// 3)通過(guò)mViewCacheExtension進(jìn)行獲取
if (holder == null && mViewCacheExtension != null) {
final View view = mViewCacheExtension
.getViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
//省略Holder的判斷邏輯
}
// 4)無(wú)路可退,通過(guò)RecyclerViewPool進(jìn)行獲取
if (holder == null) {
holder = getRecycledViewPool().getRecycledView(type);
if (holder != null) {
holder.resetInternal();
if (FORCE_INVALIDATE_DISPLAY_LIST) {
invalidateDisplayListInt(holder);
}
}
}
// 5)如果上面都沒有獲取到蛙酪,那么就說(shuō)明是第一屏齐苛,所以就得重新創(chuàng)建
if (holder == null) {
long start = getNanoTime();
//省略判空代碼
holder = mAdapter.createViewHolder(RecyclerView.this, type);
if (ALLOW_THREAD_GAP_WORK) {
// 放入最近最少使用的隊(duì)列中
RecyclerView innerView = findNestedRecyclerView(holder.itemView);
if (innerView != null) {
holder.mNestedRecyclerView = new WeakReference<>(innerView);
}
}
long end = getNanoTime();
mRecyclerPool.factorInCreateTime(type, end - start);
}
}
//在返回給LayoutManager重新繪制前,需要更新一下ViewHolder的相關(guān)信息
if (fromScrapOrHiddenOrCache && !mState.isPreLayout()
&& holder.hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
//記錄動(dòng)畫信息
if (mState.mRunSimpleAnimations) {
int changeFlags = ItemAnimator.buildAdapterChangeFlagsForAnimations(holder);
changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(
mState, holder, changeFlags, holder.getUnmodifiedPayloads());
//如果當(dāng)前的ViewHolder已經(jīng)綁定過(guò)數(shù)據(jù)滤否,那么記錄一下動(dòng)畫信息
recordAnimationInfoIfBouncedHiddenView(holder, info);
}
}
boolean bound = false;
if (mState.isPreLayout() && holder.isBound()) {
//綁定過(guò)數(shù)據(jù)的ViewHolder
holder.mPreLayoutPosition = position;
} else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
//未綁定數(shù)據(jù)的ViewHolder需要進(jìn)行數(shù)據(jù)綁定
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
}
// 將ViewHolder設(shè)置給ViewGroup.LayoutParams
final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
final LayoutParams rvLayoutParams;
if (lp == null) {
rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
holder.itemView.setLayoutParams(rvLayoutParams);
} else if (!checkLayoutParams(lp)) {
rvLayoutParams = (LayoutParams) generateLayoutParams(lp);
holder.itemView.setLayoutParams(rvLayoutParams);
} else {
rvLayoutParams = (LayoutParams) lp;
}
rvLayoutParams.mViewHolder = holder;
rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound;
return holder;
}
將AttachedView進(jìn)行緩存
void scrapView(View view) {
final ViewHolder holder = getChildViewHolderInt(view);
//數(shù)據(jù)源未改變脸狸,放入mAttachedScrap
if (holder.hasAnyOfTheFlags(ViewHolder.FLAG_REMOVED | ViewHolder.FLAG_INVALID)
|| !holder.isUpdated() || canReuseUpdatedViewHolder(holder)) {
//省略部分邏輯判斷代碼
holder.setScrapContainer(this, false);
mAttachedScrap.add(holder);
} else {
//數(shù)據(jù)源未改變最仑,放入mAttachedScrap
if (mChangedScrap == null) {
mChangedScrap = new ArrayList<ViewHolder>();
}
holder.setScrapContainer(this, true);
mChangedScrap.add(holder);
}
}
清空
clear
將CachedView添加進(jìn)ViewHolder藐俺,并且清空
public void clear() {
mAttachedScrap.clear();
recycleAndClearCachedViews();
}
clearScrap
清空緩存中可見的Scrap數(shù)組
void clearScrap() {
mAttachedScrap.clear();
if (mChangedScrap != null) {
mChangedScrap.clear();
}
}
定向刷新
可以對(duì)指定的Item進(jìn)行刷新炊甲,是RecyclerView的又一特點(diǎn),RecyclerView在觀察者模式中的Observer中新增了很多方法欲芹,用于定向刷新卿啡,這個(gè)在前面的觀察者模式中已經(jīng)提到過(guò),下面從源碼的角度分析一下原理菱父,我們拿adapter.notifyItemChanged(0),最終會(huì)RecyclerViewDataObserver的方法
onItemRangeChanged
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
assertNotInLayoutOrScroll(null);
//繼續(xù)追蹤mAdapterHelper
if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
triggerUpdateProcessor();
}
}
首先調(diào)用了AdapterHelper的onItemRangeChanged方法颈娜,這里簡(jiǎn)單說(shuō)一下AdapterHelper,用來(lái)幫助Adapter更新數(shù)組的浙宜,類似于ChildHelper幫助LayoutManager進(jìn)行布局
boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) {
if (itemCount < 1) {
return false;
}
//將需要更新的Item的范圍記錄下來(lái)官辽,并添加更新標(biāo)識(shí)
mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount, payload));
mExistingUpdateTypes |= UpdateOp.UPDATE;
return mPendingUpdates.size() == 1;
}
然后接著調(diào)用了triggerUpdateProcessor方法
void triggerUpdateProcessor() {
if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
//如果有動(dòng)畫,先執(zhí)行動(dòng)畫粟瞬,然后再進(jìn)行測(cè)量
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
//重新測(cè)量
requestLayout();
}
}
其實(shí)看到這里同仆,發(fā)現(xiàn)AdapterHelper只是記錄了需要刷新的Item的范圍,然后就開始進(jìn)行重新測(cè)量了裙品,那么很明顯俗批,不管是全部刷新還是指定范圍的定向刷新,RecyclerView都是需要重新測(cè)量的市怎,所以岁忘,定向刷新的真正語(yǔ)義是對(duì)指定范圍的ViewHolder進(jìn)行數(shù)據(jù)刷新,不會(huì)像ListView一樣区匠,刷新所有的干像,所以我們只能從布局的時(shí)候來(lái)查找,RecyclerView的布局是交給LayoutMananger來(lái)進(jìn)行布局的驰弄,那么根據(jù)AbsListView一樣蝠筑,LayoutMananger一定會(huì)在自身當(dāng)中定義一些公共方法給子類實(shí)現(xiàn),這個(gè)方法實(shí)際上就是onLayoutChildren揩懒,還有onLayoutCompleted什乙,是在繪制完成的時(shí)候進(jìn)行調(diào)用,進(jìn)行資源清理
public void onLayoutChildren(Recycler recycler, State state) {
Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
}
//做一些清理工作
public void onLayoutCompleted(State state) {
}
我們繼續(xù)查看子類中的實(shí)現(xiàn)
onLayoutChildren
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
// layout algorithm:
// 1) by checking children and other variables, find an anchor coordinate and an anchor
// item position.
// 2) fill towards start, stacking from bottom
// 3) fill towards end, stacking from top
// 4) scroll to fulfill requirements like stack from bottom.
//開始布局
fill(recycler, mLayoutState, state, false);
}
繼續(xù)追蹤fill方法
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
// max offset we should set is mFastScroll + available
final int start = layoutState.mAvailable;
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
// TODO ugly bug fix. should not happen
if (layoutState.mAvailable < 0) {
layoutState.mScrollingOffset += layoutState.mAvailable;
}
recycleByLayoutState(recycler, layoutState);
}
int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
layoutChunkResult.resetInternal();
if (VERBOSE_TRACING) {
TraceCompat.beginSection("LLM LayoutChunk");
}
//循環(huán)中調(diào)用了此方法
layoutChunk(recycler, state, layoutState, layoutChunkResult);
if (VERBOSE_TRACING) {
TraceCompat.endSection();
}
if (layoutChunkResult.mFinished) {
break;
}
layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
|| !state.isPreLayout()) {
layoutState.mAvailable -= layoutChunkResult.mConsumed;
// we keep a separate remaining space because mAvailable is important for recycling
remainingSpace -= layoutChunkResult.mConsumed;
}
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
if (layoutState.mAvailable < 0) {
layoutState.mScrollingOffset += layoutState.mAvailable;
}
recycleByLayoutState(recycler, layoutState);
}
if (stopOnFocusable && layoutChunkResult.mFocusable) {
break;
}
}
if (DEBUG) {
validateChildOrder();
}
return start - layoutState.mAvailable;
}
繼續(xù)追蹤layoutChunk
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
View view = layoutState.next(recycler);
if (view == null) {
if (DEBUG && layoutState.mScrapList == null) {
throw new RuntimeException("received null view when unexpected");
}
result.mFinished = true;
return;
}
LayoutParams params = (LayoutParams) view.getLayoutParams();
if (layoutState.mScrapList == null) {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addView(view);
} else {
addView(view, 0);
}
} else {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addDisappearingView(view);
} else {
addDisappearingView(view, 0);
}
}
measureChildWithMargins(view, 0, 0);
result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
int left, top, right, bottom;
if (mOrientation == VERTICAL) {
if (isLayoutRTL()) {
right = getWidth() - getPaddingRight();
left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
} else {
left = getPaddingLeft();
right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
}
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
bottom = layoutState.mOffset;
top = layoutState.mOffset - result.mConsumed;
} else {
top = layoutState.mOffset;
bottom = layoutState.mOffset + result.mConsumed;
}
} else {
top = getPaddingTop();
bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
right = layoutState.mOffset;
left = layoutState.mOffset - result.mConsumed;
} else {
left = layoutState.mOffset;
right = layoutState.mOffset + result.mConsumed;
}
}
layoutDecoratedWithMargins(view, left, top, right, bottom);
if (params.isItemRemoved() || params.isItemChanged()) {
result.mIgnoreConsumed = true;
}
result.mFocusable = view.hasFocusable();
}
我看了一遍已球,這里面并沒有給出是否BindViewHolder的方法臣镣,所以最開始的思路錯(cuò)了,應(yīng)該是在layoutChildren調(diào)用之前就已經(jīng)確定是否綁定智亮,然后我們發(fā)現(xiàn)layoutChildren是在dispatchLayoutStep1/2/3中都有調(diào)用忆某,這個(gè)就尷尬了,不過(guò)我們繼續(xù)查看此方法阔蛉,發(fā)現(xiàn)不僅僅這三個(gè)方法在會(huì)在dispatchLayout中被調(diào)用到弃舒,
void dispatchLayout() {
if (mAdapter == null) {
Log.e(TAG, "No adapter attached; skipping layout");
// leave the state in START
return;
}
if (mLayout == null) {
Log.e(TAG, "No layout manager attached; skipping layout");
// leave the state in START
return;
}
mState.mIsMeasuring = false;
if (mState.mLayoutStep == State.STEP_START) {
dispatchLayoutStep1();
mLayout.setExactMeasureSpecsFrom(this);
dispatchLayoutStep2();
} else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth() ||
mLayout.getHeight() != getHeight()) {
// First 2 steps are done in onMeasure but looks like we have to run again due to
// changed size.
mLayout.setExactMeasureSpecsFrom(this);
dispatchLayoutStep2();
} else {
// always make sure we sync them (to ensure mode is exact)
mLayout.setExactMeasureSpecsFrom(this);
}
dispatchLayoutStep3();
}
而dispatchLayout這個(gè)方法在onLayout中單獨(dú)進(jìn)行調(diào)用,而且只調(diào)用了這一個(gè)方法,那么沒有疑問(wèn)聋呢,應(yīng)該是在布局的時(shí)候來(lái)判斷苗踪,所以先查看一下dispatchLayoutStep這三兄弟
根據(jù)注釋,我們是在第一個(gè)方法里面進(jìn)行ViewHolder的更新削锰,所以我們重點(diǎn)查看一下dispatchLayoutStep1這個(gè)方法
dispatchLayoutStep1
private void dispatchLayoutStep1() {
mState.assertLayoutStep(State.STEP_START);
mState.mIsMeasuring = false;
eatRequestLayout();
mViewInfoStore.clear();
onEnterLayoutOrScroll();
saveFocusInfo();
//命名太規(guī)范了通铲,一下子就找到了,繼續(xù)追蹤
processAdapterUpdatesAndSetAnimationFlags();
}
processAdapterUpdatesAndSetAnimationFlags
private void processAdapterUpdatesAndSetAnimationFlags() {
if (mDataSetHasChangedAfterLayout) {
// Processing these items have no value since data set changed unexpectedly.
// Instead, we just reset it.
mAdapterHelper.reset();
markKnownViewsInvalid();
//最開始以為是在這個(gè)方法中進(jìn)行回調(diào)的器贩,后來(lái)發(fā)現(xiàn)在子類是空實(shí)現(xiàn)颅夺,
//也就是說(shuō)這個(gè)方法供使用著自己定義,通知回調(diào)
mLayout.onItemsChanged(this);
}
if (predictiveItemAnimationsEnabled()) {
mAdapterHelper.preProcess();
} else {
//更新ViewHolder
mAdapterHelper.consumeUpdatesInOnePass();
}
//省略動(dòng)畫相關(guān)操作
}
consumeUpdatesInOnePass
void consumeUpdatesInOnePass() {
consumePostponedUpdates();
final int count = mPendingUpdates.size();
for (int i = 0; i < count; i++) {
UpdateOp op = mPendingUpdates.get(i);
switch (op.cmd) {
case UpdateOp.ADD:
mCallback.onDispatchSecondPass(op);
mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
break;
case UpdateOp.REMOVE:
mCallback.onDispatchSecondPass(op);
mCallback.offsetPositionsForRemovingInvisible(op.positionStart, op.itemCount);
break;
case UpdateOp.UPDATE:
//接口回調(diào)
mCallback.onDispatchSecondPass(op);
mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount, op.payload);
break;
case UpdateOp.MOVE:
mCallback.onDispatchSecondPass(op);
mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
break;
}
if (mOnItemProcessedCallback != null) {
mOnItemProcessedCallback.run();
}
}
//這里也是回收蛹稍,實(shí)際上這個(gè)回收不是循環(huán)利用吧黄,就是清空的意思
recycleUpdateOpsAndClearList(mPendingUpdates);
mExistingUpdateTypes = 0;
}
這個(gè)接口是初始化的時(shí)候傳入的,我們追蹤此接口
@Override
public void markViewHoldersUpdated(int positionStart, int itemCount, Object payload) {
//這個(gè)方法才是真正的更新Adapter的ViewHolder唆姐,繼續(xù)跟進(jìn)
viewRangeUpdate(positionStart, itemCount, payload);
mItemsChanged = true;
}
@Override
public void onDispatchFirstPass(UpdateOp op) {
//他的方法都是空實(shí)現(xiàn)稚字,子類也沒有實(shí)現(xiàn),我們可以復(fù)寫來(lái)接收通知
dispatchUpdate(op);
}
void dispatchUpdate(UpdateOp op) {
switch (op.cmd) {
case UpdateOp.ADD:
mLayout.onItemsAdded(RecyclerView.this, op.positionStart, op.itemCount);
break;
case UpdateOp.REMOVE:
mLayout.onItemsRemoved(RecyclerView.this, op.positionStart, op.itemCount);
break;
case UpdateOp.UPDATE:
mLayout.onItemsUpdated(RecyclerView.this, op.positionStart, op.itemCount,
op.payload);
break;
case UpdateOp.MOVE:
mLayout.onItemsMoved(RecyclerView.this, op.positionStart, op.itemCount, 1);
break;
}
}
viewRangeUpdate
通過(guò)名字也能很好的查看出厦酬,進(jìn)行范圍內(nèi)的更新
void viewRangeUpdate(int positionStart, int itemCount, Object payload) {
final int childCount = mChildHelper.getUnfilteredChildCount();
final int positionEnd = positionStart + itemCount;
for (int i = 0; i < childCount; i++) {
final View child = mChildHelper.getUnfilteredChildAt(i);
final ViewHolder holder = getChildViewHolderInt(child);
if (holder == null || holder.shouldIgnore()) {
continue;
}
if (holder.mPosition >= positionStart && holder.mPosition < positionEnd) {
//添加Flag,這個(gè)是從緩存中獲取到ViewHolder的時(shí)候胆描,需不需要刷新的唯一標(biāo)識(shí)
holder.addFlags(ViewHolder.FLAG_UPDATE);
//添加payLoad參數(shù),用于ViewHolder的局部刷新
holder.addChangePayload(payload);
// lp cannot be null since we get ViewHolder from it.
((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
}
}
//更新緩存中的標(biāo)記位仗阅,便于數(shù)據(jù)源不改變的時(shí)候直接復(fù)用
mRecycler.viewRangeUpdate(positionStart, itemCount);
}
局部刷新
在上面提到過(guò)昌讲,我們刷新的時(shí)候會(huì)調(diào)用一個(gè)方法,叫做notifyItemChanged减噪,通常會(huì)傳一個(gè)起始位置以及范圍短绸,其實(shí)我們還可以傳入一個(gè)Object類型的參數(shù),
public final void notifyItemChanged(int position, Object payload) {
mObservable.notifyItemRangeChanged(position, 1, payload);
}
如何獲取呢筹裕,也很簡(jiǎn)單醋闭,復(fù)寫onBindViewHolder這個(gè)方法,
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads) {
//點(diǎn)進(jìn)去會(huì)進(jìn)入下面的方法
super.onBindViewHolder(holder, position, payloads);
}
默認(rèn)的實(shí)現(xiàn)是onBindViewHolder朝卒,也就是我們經(jīng)常復(fù)寫的方法
public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
onBindViewHolder(holder, position);
}
這個(gè)參數(shù)有什么用证逻,就是用于局部刷新的,比如一個(gè)ViewHolder展示了很多關(guān)于學(xué)生的數(shù)據(jù)抗斤,姓名囚企,年齡,愛好瑞眼,學(xué)習(xí)成績(jī)龙宏,家庭住址等,但是我們只需要刷新一下學(xué)習(xí)成績(jī)伤疙,我們?cè)谡{(diào)用notifyItemChanged的時(shí)候只需要調(diào)用一下otifyItemChanged(0, score),然后在Adapter中進(jìn)行獲取就好了刷新這一項(xiàng)就好了银酗。
DiffUtil
當(dāng)我們知道我們要刷新的Item的范圍的時(shí)候,可以直接調(diào)用notifyItemChanged(position,range),當(dāng)我們更新前后的集合數(shù)據(jù)差不多黍特,只有部分差異的時(shí)候蛙讥,我們都還是調(diào)用的是notifyDataSetChanged,即全量刷新衅澈,其實(shí)谷歌已經(jīng)意識(shí)到這個(gè)問(wèn)題键菱,已經(jīng)在support包中提供了一個(gè)工具類DiffUtil谬墙,可以幫助我們進(jìn)行分析對(duì)比前后的數(shù)據(jù)差異今布,從而進(jìn)行定向刷新,我們來(lái)分析一下這個(gè)工具類:
注釋
DiffUtil is a utility class that can calculate the difference between two lists and output a list of update operations that converts the first list into the second one.
Diffutil是一個(gè)可以計(jì)算出兩個(gè)集合之間的差別并且輸出從一個(gè)集合到另外一個(gè)集合之間的變化的工具類
從注釋可以看出拭抬,DifUtil的主要操作主要是用來(lái)比對(duì)兩個(gè)新舊集合之間產(chǎn)生的差異部默,同時(shí)DiffUtil底層采用的是Myers差分算法,大家可以先看看這篇文章了解一下Myers差分算法造虎,不然下面的肯定很不好理解傅蹂,說(shuō)通俗一點(diǎn)就現(xiàn)在有新舊兩個(gè)集合,長(zhǎng)度分別為M算凿、N份蝴,是通過(guò)構(gòu)造一個(gè)平面直角坐標(biāo)系,然后X軸向右氓轰,用原數(shù)據(jù)集合中的元素填充橫坐標(biāo)婚夫,Y軸向下,新數(shù)據(jù)集合中的元素填充縱坐標(biāo)署鸡,
然后需要計(jì)算從坐標(biāo)(0,0)到(M,N)的最短路徑案糙,圖中的對(duì)角線表示元素相同,如果走對(duì)角線的話不算在總長(zhǎng)度內(nèi)靴庆,因?yàn)闆]有出現(xiàn)差分时捌,說(shuō)白了也就是只要兩個(gè)集合中有相同的元素,那么就會(huì)在舊集合中進(jìn)行刪除或者添加操作炉抒,從而最大限度的保證從舊集合到新集合的定向刷新奢讨。
成員變量
private static final Comparator<Snake> SNAKE_COMPARATOR = new Comparator<Snake>() {
@Override
public int compare(Snake o1, Snake o2) {
int cmpX = o1.x - o2.x;
return cmpX == 0 ? o1.y - o2.y : cmpX;
}
};
定義了一個(gè)比較器,傳入了snake(下面分析)泛型焰薄,其實(shí)Diffutil只有這一個(gè)成員變量禽笑,然后就是內(nèi)部類,下面先分析一下內(nèi)部類:
Callback
public abstract static class Callback {
public abstract int getOldListSize();
public abstract int getNewListSize();
//Item是否一致
public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition);
//Item的內(nèi)容是否一致
public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition);
//看到Payload應(yīng)該很容易之前研究RecyclerView的局部刷新中的payLoad蛤奥,其實(shí)是一個(gè)意思
//這個(gè)會(huì)傳到onBindHolder中去佳镜,作為局部刷新的標(biāo)志
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
return null;
}
}
Snake
這個(gè)直接翻譯過(guò)來(lái)是一條蛇,通過(guò)注釋來(lái)看凡桥,DiffUtil是采用了Myers差分算法蟀伸,所以Snake實(shí)際上代表的是蛇形路徑,就是將新舊集合的差異進(jìn)一步描述出來(lái)
static class Snake {
int x;//Position in the old list
int y;//Position in the new list
int size;// Number of matches. Might be 0
boolean removal;//true代表移除,false代表新增
//true, 表示在尾部添加或者移除
//false,表示在頭部進(jìn)行添加或刪除
boolean reverse;
}
如果你稍微了解一下Myers差分算法啊掏,就比較好理解蠢络,因?yàn)樾录鲜窃谂f集合的基礎(chǔ)上進(jìn)行變化得到的,通過(guò)增加迟蜜,刪除乃至移動(dòng)變換刹孔,所以需要知道是在當(dāng)前元素的前面或者后面進(jìn)行操作,并且需要知道是什么操作娜睛,增加或者刪除等
Range
static class Range {
int oldListStart, oldListEnd;
int newListStart, newListEnd;
public Range() {
}
public Range(int oldListStart, int oldListEnd, int newListStart, int newListEnd) {
this.oldListStart = oldListStart;
this.oldListEnd = oldListEnd;
this.newListStart = newListStart;
this.newListEnd = newListEnd;
}
}
貌似不需要注釋髓霞,很好理解,主要是記錄Snake的全局坐標(biāo)
DiffResult
這個(gè)是DiffResult的核心方法
public static DiffResult calculateDiff(Callback cb, boolean detectMoves) {
final int oldSize = cb.getOldListSize();
final int newSize = cb.getNewListSize();
//Snake集合
final List<Snake> snakes = new ArrayList<>();
//Range集合
final List<Range> stack = new ArrayList<>();
stack.add(new Range(0, oldSize, 0, newSize));
final int max = oldSize + newSize + Math.abs(oldSize - newSize);
final int[] forward = new int[max * 2];
final int[] backward = new int[max * 2];
// We pool the ranges to avoid allocations for each recursive call.
final List<Range> rangePool = new ArrayList<>();
while (!stack.isEmpty()) {
final Range range = stack.remove(stack.size() - 1);
//采用Myers差分算法計(jì)算差分結(jié)果
final Snake snake = diffPartial(cb, range.oldListStart, range.oldListEnd,
range.newListStart, range.newListEnd, forward, backward, max);
//省略了一些算法畦戒,感興趣的可以自己去看源碼
}
// 對(duì)差分結(jié)果進(jìn)行排序
Collections.sort(snakes, SNAKE_COMPARATOR);
return new DiffResult(cb, snakes, forward, backward, detectMoves);
}
dispatchUpdatesTo
調(diào)用此方法方库,可以進(jìn)行刷新操作
public void dispatchUpdatesTo(final RecyclerView.Adapter adapter) {
//繼續(xù)跟蹤dispatchUpdatesTo方法
dispatchUpdatesTo(new ListUpdateCallback() {
@Override
public void onInserted(int position, int count) {
adapter.notifyItemRangeInserted(position, count);
}
@Override
public void onRemoved(int position, int count) {
adapter.notifyItemRangeRemoved(position, count);
}
@Override
public void onMoved(int fromPosition, int toPosition) {
adapter.notifyItemMoved(fromPosition, toPosition);
}
@Override
public void onChanged(int position, int count, Object payload) {
adapter.notifyItemRangeChanged(position, count, payload);
}
});
}
看完你會(huì)發(fā)現(xiàn),就是一個(gè)回調(diào)接口障斋,來(lái)回調(diào)adapter而已
dispatchUpdatesTo
根據(jù)計(jì)算得到的路徑纵潦,來(lái)對(duì)舊集合定向刷新
public void dispatchUpdatesTo(ListUpdateCallback updateCallback) {
final BatchingListUpdateCallback batchingCallback;
if (updateCallback instanceof BatchingListUpdateCallback) {
batchingCallback = (BatchingListUpdateCallback) updateCallback;
} else {
batchingCallback = new BatchingListUpdateCallback(updateCallback);
updateCallback = batchingCallback;
}
final List<PostponedUpdate> postponedUpdates = new ArrayList<>();
int posOld = mOldListSize;
int posNew = mNewListSize;
//遍歷通過(guò)Myers差分算法生成最短路徑,由于是集合所以要從頭開始計(jì)算的話需要倒序遍歷
for (int snakeIndex = mSnakes.size() - 1; snakeIndex >= 0; snakeIndex--) {
final Snake snake = mSnakes.get(snakeIndex);
final int snakeSize = snake.size;
final int endX = snake.x + snakeSize;
final int endY = snake.y + snakeSize;
if (endX < posOld) {
//右移垃环,說(shuō)明是刪除操作邀层,進(jìn)行相應(yīng)處理
dispatchRemovals(postponedUpdates, batchingCallback, endX, posOld - endX, endX);
}
if (endY < posNew) {
//下移,說(shuō)明是新增操作遂庄,進(jìn)行相應(yīng)處理
dispatchAdditions(postponedUpdates, batchingCallback, endX, posNew - endY,
endY);
}
//如果復(fù)寫了DiffUtil.Callback的getChangePayload方法寥院,將會(huì)支持RecyclerView的局部刷新
for (int i = snakeSize - 1; i >= 0; i--) {
if ((mOldItemStatuses[snake.x + i] & FLAG_MASK) == FLAG_CHANGED) {
//分發(fā)DiffUtil.Callback的getChangePayload方法,最終會(huì)抵達(dá)adater
batchingCallback.onChanged(snake.x + i, 1,
mCallback.getChangePayload(snake.x + i, snake.y + i));
}
}
posOld = snake.x;
posNew = snake.y;
}
//回調(diào)Adapter,跟一下源碼
batchingCallback.dispatchLastEvent();
}
dispatchLastEvent
public void dispatchLastEvent() {
if (mLastEventType == TYPE_NONE) {
return;
}
switch (mLastEventType) {
case TYPE_ADD:
mWrapped.onInserted(mLastEventPosition, mLastEventCount);
break;
case TYPE_REMOVE:
mWrapped.onRemoved(mLastEventPosition, mLastEventCount);
break;
case TYPE_CHANGE:
mWrapped.onChanged(mLastEventPosition, mLastEventCount, mLastEventPayload);
break;
}
mLastEventPayload = null;
mLastEventType = TYPE_NONE;
}
mWrapped實(shí)際上就是DiffUtil.Callback的實(shí)例涧团,然后我們調(diào)用的時(shí)候傳入自定義的Callback接口只磷,就好
用法簡(jiǎn)介
上面說(shuō)了很多原理,下面簡(jiǎn)單介紹一下用法
- 自定義DiffCallback繼承自DiffUtil.Callback,復(fù)寫相關(guān)方法
- 調(diào)用DiffUtil.calculateDiff(new DiffCallBack(oldData, newData), true)泌绣,生成diffResult;
- 調(diào)用diffResult.dispatchUpdatesTo(mAdapter);
如果還有不熟悉的話可以直接在網(wǎng)上搜相關(guān)博客钮追,用法還是比較簡(jiǎn)單的,不過(guò)有一點(diǎn)需要注意的是就是阿迈,在DiffUtil的注釋當(dāng)中元媚,說(shuō)明Myers差分算法本身是不支持item移動(dòng)的,然后谷歌自己實(shí)現(xiàn)了一套算法苗沧,支持item的移動(dòng)刊棕,但是當(dāng)數(shù)據(jù)量比較大的時(shí)候比較耗時(shí),需要在子線程中進(jìn)行計(jì)算待逞,我個(gè)人覺得只要當(dāng)前后兩個(gè)數(shù)據(jù)集合相似度較高的時(shí)候DiffUtil的效果會(huì)比較明顯甥角,這種情況下的定向刷新比較有意義。
總結(jié)
上面分析了一下ListView跟RecyclerView的緩存原理识樱,下面簡(jiǎn)單對(duì)比分析一下
AbsListView | RecyclerView | |
---|---|---|
緩存 | View | ViewHolder |
定向刷新 | 不支持 | 支持 |
局部刷新 | 不支持 | 支持 |
刷新動(dòng)畫 | 不支持 | 支持 |
Item點(diǎn)擊 | 支持 | 不支持 |
分隔線 | 樣式單一 | 自定義樣式 |
布局方式 | 列表/網(wǎng)格 | 自定義樣式 |
頭尾添加 | 支持 | 不支持 |
通過(guò)分析可能大家已經(jīng)知道了RecyclerView相對(duì)于ListView的區(qū)別嗤无,如果我們需要頻繁的刷新列表數(shù)據(jù)以及添加動(dòng)畫的話震束,我們還是采用RecyclerView,對(duì)于前后數(shù)據(jù)量變化不大的新舊集合当犯,還可以通過(guò)DiffUtil來(lái)進(jìn)行差分垢村,這樣就能夠?qū)崿F(xiàn)定向刷新以及局部刷新,否則建議使用ListView嚎卫,因?yàn)長(zhǎng)istView內(nèi)置了很多Adapter嘉栓,類似ArrayAdapter,SimpleAdapter,CursorAdapter拓诸。
還有一種情況就是宮格列切換的功能侵佃,如下圖
那個(gè)時(shí)候RecyclerView剛出來(lái),最開始是用ListView跟GridView實(shí)現(xiàn)的,就是搞了兩個(gè)恰响,一個(gè)顯示趣钱,另外一個(gè)隱藏涌献,由于當(dāng)時(shí)是采用的SwipeRefreshLayout實(shí)現(xiàn)的胚宦,所以自定義的上拉加載,在ListView跟GridView之間切換的時(shí)候特別痛苦燕垃,后來(lái)采用了RecyclerView枢劝,真的是不要太簡(jiǎn)單,動(dòng)態(tài)設(shè)置一下LayoutManager就行卜壕,底部的加載進(jìn)度條也可以通過(guò)設(shè)置GridLayoutManager.SpanSizeLookup可以很好的進(jìn)行處理您旁。