一.RecyclerView顯示
??????在使用RecyclerView時(shí)义锥,需要結(jié)合Adapter來(lái)使用,一個(gè)RecyclerView需要一個(gè)Adapter蕉朵,一個(gè)Adapter中對(duì)應(yīng)著指定數(shù)量及指定type的item闻丑,即ViewHolder十兢,那ViewHolder時(shí)如何創(chuàng)建及如何顯示的?
??????在平時(shí)使用中耘擂,通過(guò)調(diào)用RecycerView.setAdapter()來(lái)顯示內(nèi)容胆剧,一起看一下執(zhí)行流程:
a.RecyclerView
public void setAdapter(@Nullable Adapter adapter) {
......
setAdapterInternal(adapter, false, true);
requestLayout();
}
??????先執(zhí)行了setAdapterInternal(adapter, false, true),看一下這個(gè)方法主要做了什么:
private void setAdapterInternal(@Nullable Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {
if (mAdapter != null) {
mAdapter.unregisterAdapterDataObserver(mObserver);
mAdapter.onDetachedFromRecyclerView(this);
}
if (!compatibleWithPrevious || removeAndRecycleViews) {
removeAndRecycleViews();
}
mAdapterHelper.reset();
final Adapter oldAdapter = mAdapter;
mAdapter = adapter;
if (adapter != null) {
adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this);
}
if (mLayout != null) {
Layout.onAdapterChanged(oldAdapter, mAdapter);
}
mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
mState.mStructureChanged = true;
}
private final RecyclerViewDataObserver mObserver = new RecyclerViewDataObserver();
??????可以看到在setAdapterInternal()里面先調(diào)用了unRegister和unDetached操作醉冤,接著執(zhí)行了register和detached操作秩霍,register里面會(huì)執(zhí)行adapter里面對(duì)應(yīng)的register方法:
private final AdapterDataObservable mObservable = new AdapterDataObservable();
public void registerAdapterDataObserver(@NonNull AdapterDataObserver observer) {
mObservable.registerObserver(observer);
}
public final void notifyDataSetChanged() {
mObservable.notifyChanged();
}
public final void notifyItemChanged(int position) {
mObservable.notifyItemRangeChanged(position, 1);
}
??????在registerAdapterDataObserver()里面執(zhí)行的是AdapterDataObservable的registerObserver()方法,當(dāng)我們?cè)诒镜貙?shí)現(xiàn)的adapter內(nèi)部執(zhí)行notifyDataSetChanged()及notifyItemChanged()等操作時(shí)蚁阳,先會(huì)調(diào)用到AdapterDataObservable內(nèi)部對(duì)應(yīng)的方法:
static class AdapterDataObservable extends Observable<AdapterDataObserver> {
public void notifyChanged() {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
}
}
public void notifyItemRangeInserted(int positionStart, int itemCount) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
}
}
........
........
}
??????在AdapterDataObservable里面最終會(huì)調(diào)用到RecyclerViewDataObserver里面對(duì)應(yīng)的方法铃绒,執(zhí)行view刷新的操作。
private class RecyclerViewDataObserver extends AdapterDataObserver {
@Override
public void onChanged() {
......
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
......
}
......
......
void triggerUpdateProcessor() {
......
}
}
??????接著上面setAdapter()內(nèi)部邏輯講韵吨,然后調(diào)用了requestLayout()匿垄,調(diào)用該方法后移宅,會(huì)執(zhí)行onLayout():
protected void onLayout(boolean changed, int l, int t, int r, int b) {
dispatchLayout();
}
??????在dispatchLayout()內(nèi)部會(huì)調(diào)用到dispatchLayoutStep2():
private void dispatchLayoutStep2() {
......
mState.mItemCount = mAdapter.getItemCount();
// Step 2: Run layout
mState.mInPreLayout = false;
mLayout.onLayoutChildren(mRecycler, mState);
....
}
??????在該方法內(nèi)部,先通過(guò)adapter的getItemCount()來(lái)獲取到item的數(shù)量椿疗,賦值給State()的mItemCount漏峰,后續(xù)在顯示item時(shí)會(huì)使用到;然后調(diào)用mLayout的onLayoutChildren()方法届榄,mLayout是在初始化RecyclerView時(shí)通過(guò)setLayoutManager()傳入的LayoutManager浅乔,此處分析LinerLayoutManager。
b.LinerLayoutManager
??????LayoutManager是用來(lái)決定RecyclerView內(nèi)部item顯示的布局铝条,LinerLayoutManager線(xiàn)性布局靖苇,GridLayoutManager是網(wǎng)格布局,本文主要分析LinerLayoutManager:
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
.......
......
fill(recycler, mLayoutState, state, false);
......
......
}
??????在onLayoutChildren()內(nèi)調(diào)用了fill()方法:
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,RecyclerView.State state, boolean stopOnFocusable) {
......
......
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
.......
layoutChunk(recycler, state, layoutState, layoutChunkResult);
.......
}
}
??????在fill()內(nèi)部通過(guò)while()來(lái)不斷的執(zhí)行l(wèi)ayoutChunk()班缰,先看一下while()循環(huán)的條件之一:layoutState.hasMore(state)贤壁,LayoutState是LinerLayoutManager內(nèi)部的一個(gè)靜態(tài)類(lèi),State是RecyclerView內(nèi)部的一個(gè)類(lèi)埠忘,注意別搞混了脾拆;
boolean hasMore(RecyclerView.State state) {
return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount();
}
??????通過(guò)以上看到,用到了getItemCount()莹妒,通過(guò)position跟item的數(shù)量來(lái)判斷是否需要繼續(xù)循環(huán)執(zhí)行l(wèi)ayoutChunk():
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
View view = layoutState.next(recycler);
......
RecyclerView.LayoutParams params = (RecyclerView.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;
}
}
// Consume the available space if the view is not removed OR changed
if (params.isItemRemoved() || params.isItemChanged()) {
result.mIgnoreConsumed = true;
}
result.mFocusable = view.hasFocusable();
}
??????在layoutChunk()內(nèi)部會(huì)通過(guò)layoutState.next(recycler)來(lái)獲取當(dāng)前position對(duì)應(yīng)的view名船,然后通過(guò)addView(),最后執(zhí)行l(wèi)ayout來(lái)進(jìn)行放置旨怠,這里我們不去關(guān)心是如何放置的渠驼,只關(guān)心是如何獲取的item對(duì)應(yīng)的view,主要看一下layoutState.next(recycler):
View next(RecyclerView.Recycler recycler) {
........
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
}
??????next()內(nèi)部最終是通過(guò)recycler的getViewForPosition來(lái)獲取view鉴腻。
c.RecyclerView.Recycler
public View getViewForPosition(int position) {
return getViewForPosition(position, false);
}
View getViewForPosition(int position, boolean dryRun) {
return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
}
??????從getViewForPosition()內(nèi)部邏輯來(lái)看迷扇,先調(diào)用tryGetViewHolderForPositionByDeadline()來(lái)獲取到ViewHolder,然后獲取到ViewHolder里面的itemView拘哨,先看一下tryGetViewHolderForPositionByDeadline():
ViewHolder tryGetViewHolderForPositionByDeadline(int position,boolean dryRun, long deadlineNs) {
boolean fromScrapOrHiddenOrCache = false;
ViewHolder holder = null;
// 0) If there is a changed scrap, try to find from there
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
// 1) Find by position from scrap/hidden list/cache
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
.......
}
if (holder == null) {
//獲取到position對(duì)應(yīng)的type谋梭,需要本地實(shí)現(xiàn),不實(shí)現(xiàn)的話(huà)倦青,默認(rèn)返回0瓮床,即一個(gè)type
final int type = mAdapter.getItemViewType(offsetPosition);
// 2) Find from scrap/cache via stable ids, if exists
if (mAdapter.hasStableIds()) {
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),type, dryRun);
......
}
if (holder == null && mViewCacheExtension != null) {
final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
......
}
if (holder == null) { // fallback to pool
holder = getRecycledViewPool().getRecycledView(type);
.....
}
if (holder == null) {
.......
holder = mAdapter.createViewHolder(RecyclerView.this, type);
.......
}
}
......
......
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
........
return holder;
}
??????從上面可以看到,去獲取holder時(shí)产镐,會(huì)經(jīng)歷四步判斷隘庄,即對(duì)應(yīng)著四級(jí)緩存,下一節(jié)會(huì)詳細(xì)介紹癣亚,如果從四級(jí)緩存中都沒(méi)有獲取到時(shí)丑掺,會(huì)執(zhí)行createViewHolder()來(lái)進(jìn)行創(chuàng)建:
??????注意一下:viewType是從本地實(shí)現(xiàn)的getItemType(postion)來(lái)獲取的,如果沒(méi)有實(shí)現(xiàn)述雾,則默認(rèn)為0街州,即都是統(tǒng)一類(lèi)型兼丰;如果實(shí)現(xiàn)了,用途有二:1.本地根據(jù)對(duì)應(yīng)的type來(lái)創(chuàng)建不同的view唆缴;2.RecyclerViewPool根據(jù)不同的type緩存對(duì)應(yīng)數(shù)量為5的viewHolder鳍征。
public final VH createViewHolder(@NonNull ViewGroup parent, int viewType) {
try {
final VH holder = onCreateViewHolder(parent, viewType);
holder.mItemViewType = viewType;
return holder;
} finally {
TraceCompat.endSection();
}
}
??????最終會(huì)回調(diào)本地adapter的onCreateViewHolder()來(lái)創(chuàng)建對(duì)應(yīng)的viewHolder,看一下ViewHolder這個(gè)類(lèi):
public abstract static class ViewHolder {
......
......
public ViewHolder(@NonNull View itemView) {
if (itemView == null) {
throw new IllegalArgumentException("itemView may not be null");
}
this.itemView = itemView;
}
......
......
}
??????通過(guò)以上可以看到面徽,itemView是在構(gòu)造方法中作為參數(shù)傳入的艳丛,在創(chuàng)建本地ViewHolder時(shí),我們會(huì)先通過(guò)LayoutInflater來(lái)inflate()對(duì)應(yīng)position及itemtype的view趟紊,然后執(zhí)行new ViewHolder(view)返回氮双,此處itemView就是我們創(chuàng)建的view。
??????接著上面分析霎匈,接下來(lái)會(huì)調(diào)用tryBindViewHolderByDeadline()戴差,先設(shè)置flag,最終會(huì)回調(diào)本地adapter的onBindViewHolder():
private boolean tryBindViewHolderByDeadline(@NonNull ViewHolder holder, int offsetPosition,
int position, long deadlineNs) {
......
final int viewType = holder.getItemViewType();
mAdapter.bindViewHolder(holder, offsetPosition);
......
return true;
}
public final void bindViewHolder(@NonNull VH holder, int position) {
holder.mPosition = position;
holder.setFlags(ViewHolder.FLAG_BOUND,
ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
}
??????以上就是在初始化RecyclerView唧躲,然后調(diào)用setAdapter()后的整個(gè)執(zhí)行流程造挽,用流程圖總結(jié)一下:
二.四級(jí)緩存機(jī)制
??????RecyclerView緩存機(jī)制分為四級(jí),分別為:Scrap(mAttachedScrap)弄痹、Cache(mCachedViews)、ViewCacheExtension(mViewCacheExtension)嵌器、RecycledViewPool(mRecyclerPool)肛真。
??????Scrap
??????就是屏幕內(nèi)的緩存數(shù)據(jù),可以直接拿來(lái)復(fù)用爽航。
??????Cache
??????剛剛移出屏幕的緩存數(shù)據(jù)蚓让,默認(rèn)大小是2個(gè),當(dāng)其容量被充滿(mǎn)同時(shí)又有新的數(shù)據(jù)添加的時(shí)候讥珍,會(huì)根據(jù)FIFO原則历极,把先進(jìn)入的緩存數(shù)據(jù)移出并放到下一級(jí)緩存中,然后再把新的數(shù)據(jù)添加進(jìn)來(lái)衷佃。Cache里面的數(shù)據(jù)是干凈的趟卸,即攜帶了原來(lái)的ViewHolder的所有數(shù)據(jù)信息,數(shù)據(jù)可以直接來(lái)拿來(lái)復(fù)用氏义。需要注意的是锄列,cache是根據(jù)position來(lái)尋找數(shù)據(jù)的,這個(gè)postion是根據(jù)第一個(gè)或者最后一個(gè)可見(jiàn)的item的position以及用戶(hù)操作行為(上拉還是下拉)惯悠。
??????舉個(gè)例子:當(dāng)前屏幕內(nèi)第一個(gè)可見(jiàn)的item的position是1邻邮,用戶(hù)進(jìn)行了一個(gè)下拉操作,那么當(dāng)前預(yù)測(cè)的position就相當(dāng)于(1-1=0)克婶,也就是position=0的那個(gè)item要被拉回到屏幕筒严,此時(shí)RecyclerView就從Cache里面找position=0的數(shù)據(jù)丹泉,如果找到了就直接拿來(lái)復(fù)用。
??????ViewCacheExtension
??????google留給開(kāi)發(fā)者自己來(lái)自定義緩存的鸭蛙,ViewCacheExtension適用場(chǎng)景:ViewHolder位置固定摹恨、內(nèi)容固定、數(shù)量有限時(shí)使用规惰。
??????使用方式如下:
//viewType類(lèi)型為T(mén)YPE_SPECIAL時(shí)睬塌,設(shè)置四級(jí)緩存池RecyclerPool不存儲(chǔ)對(duì)應(yīng)類(lèi)型的數(shù)據(jù) 因?yàn)樾枰_(kāi)發(fā)者自行緩存
recyclerView.getRecycledViewPool().setMaxRecycledViews(DemoAdapter.TYPE_SPECIAL, 0);
//設(shè)置ViewCacheExtension緩存
recyclerView.setViewCacheExtension(new MyViewCacheExtension());
//實(shí)現(xiàn)自定義緩存ViewCacheExtension
class MyViewCacheExtension extends RecyclerView.ViewCacheExtension {
@Nullable
@Override
public View getViewForPositionAndType(@NonNull RecyclerView.Recycler recycler, int position, int viewType) {
//如果viewType為T(mén)YPE_SPECIAL,使用自己緩存的View去構(gòu)建ViewHolder
// 否則返回null,會(huì)使用系統(tǒng)RecyclerPool緩存或者從新通過(guò)onCreateViewHolder構(gòu)建View及ViewHolder
return viewType == DemoAdapter.TYPE_SPECIAL ? adapter.caches.get(position) : null;
}
}
Adapter.java
public SparseArray<View> caches = new SparseArray<>();//開(kāi)發(fā)者自行維護(hù)的緩存
Adapter#onBindViewHolder中根據(jù)position設(shè)置的緩存:caches.put(position, sHolder.itemView);
三.源碼分析
??????主要分析一下RecycledViewPool的流程:
??????RecycledViewPool
??????Cache默認(rèn)的緩存數(shù)量是2個(gè)歇万,當(dāng)Cache緩存滿(mǎn)了以后會(huì)根據(jù)FIFO(先進(jìn)先出)的規(guī)則把Cache先緩存進(jìn)去的ViewHolder移出并緩存到RecycledViewPool中揩晴,RecycledViewPool存儲(chǔ)時(shí)是根據(jù)itemType來(lái)存儲(chǔ)的,每個(gè)itemType對(duì)應(yīng)的緩存數(shù)量是5個(gè)贪磺;看一下主要實(shí)現(xiàn)邏輯:
public static class RecycledViewPool {
private static final int DEFAULT_MAX_SCRAP = 5;
static class ScrapData {
final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
int mMaxScrap = DEFAULT_MAX_SCRAP;
long mCreateRunningAverageNs = 0;
long mBindRunningAverageNs = 0;
}
SparseArray<ScrapData> mScrap = new SparseArray<>();
.........
.........
}
??????RecycledViewPool內(nèi)部維護(hù)一個(gè)SparseArray<ScrapData>mScrap硫兰,ScrapData內(nèi)部維護(hù)一個(gè)ArrayList<ViewHolder>mScrapHeap,最大值為5寒锚;
public void putRecycledView(ViewHolder scrap) {
final int viewType = scrap.getItemViewType();
final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
return;
}
......
scrap.resetInternal();
scrapHeap.add(scrap);
}
private ScrapData getScrapDataForType(int viewType) {
ScrapData scrapData = mScrap.get(viewType);
if (scrapData == null) {
scrapData = new ScrapData();
mScrap.put(viewType, scrapData);
}
return scrapData;
}
??????在執(zhí)行putRecycledView(vh)時(shí)劫映,先判斷vh的type,根據(jù)type去獲取ScrapData刹前,如果獲取到就返回泳赋,否則就創(chuàng)建一個(gè)新的ScrapData,然后加入到mScrap內(nèi)喇喉,key為對(duì)應(yīng)的type祖今,再獲取到ScrapData內(nèi)部的mScrapHeap,判斷size()拣技,如果已經(jīng)等于5千诬,就不再存儲(chǔ)了,否則先執(zhí)行resetInternal()來(lái)清空數(shù)據(jù)膏斤,然后再加入到mScrapHeap里面徐绑;
public ViewHolder getRecycledView(int viewType) {
final ScrapData scrapData = mScrap.get(viewType);
if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
for (int i = scrapHeap.size() - 1; i >= 0; i--) {
if (!scrapHeap.get(i).isAttachedToTransitionOverlay()) {
return scrapHeap.remove(i);
}
}
}
return null;
}
??????在執(zhí)行g(shù)etRecycledView(type)時(shí),先根據(jù)type從mScrap內(nèi)獲取對(duì)應(yīng)的ScrapData莫辨,如果不為空傲茄,則獲取到ScrapData內(nèi)部的mScrapHeap,再?gòu)膆eap中獲取到ViewHolder衔掸,然后將對(duì)應(yīng)位置的ViewHolder刪除烫幕,后續(xù)put時(shí)再進(jìn)行添加;
??????直觀一點(diǎn)如下:
??????RecycledViewPool與Cache相比不同的是敞映,從Cache里面移出的ViewHolder在存入RecycledViewPool之前ViewHolder的數(shù)據(jù)會(huì)被全部重置(resetInternal())较曼,相當(dāng)于一個(gè)新的ViewHolder,而且Cache是根據(jù)position來(lái)獲取ViewHolder振愿,而RecycledViewPool是根據(jù)itemType獲取的捷犹,如果沒(méi)有重寫(xiě)getItemType()方法弛饭,itemType就是默認(rèn)的。因?yàn)镽ecycledViewPool緩存的ViewHolder是全新的萍歉,所以取出來(lái)的時(shí)候需要走onBindViewHolder()方法侣颂。
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
boolean fromScrapOrHiddenOrCache = false;
ViewHolder holder = null;
// 0) If there is a changed scrap, try to find from there
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
// 1) Find by position from scrap/hidden list/cache
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
......
}
if (holder == null) {
.........
// 2) Find from scrap/cache via stable ids, if exists
if (mAdapter.hasStableIds()) {
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
type, dryRun);
......
}
if (holder == null && mViewCacheExtension != null) {
final View view = mViewCacheExtension
.getViewForPositionAndType(this, position, type);
.......
}
if (holder == null) { // fallback to pool
holder = getRecycledViewPool().getRecycledView(type);
........
}
if (holder == null) {
//create view holder
holder = mAdapter.createViewHolder(RecyclerView.this, type);
.......
}
......
}
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()) {
final int offsetPosition = mAdapterHelper.findPositionOffset(position);
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
}
......
return holder;
}
??????直觀一點(diǎn)如下:
四.ReceyclerView與ListView的區(qū)別
??????1)ListView布局單一,RecycleView可以根據(jù)LayoutManger有橫向枪孩,瀑布和表格布局憔晒;
??????2)自定義適配器中,ListView的適配器繼承ArrayAdapter蔑舞;RecycleView的適配器繼承RecyclerAdapter拒担,并將范類(lèi)指定為子項(xiàng)對(duì)象類(lèi)ViewHolder(內(nèi)部類(lèi))。
??????3)ListView優(yōu)化需要自定義ViewHolder和判斷convertView是否為null(不用每次都創(chuàng)建convertView和調(diào)用findViewById()攻询,findViewById()這個(gè)方法是比較耗性能的操作从撼,因?yàn)檫@個(gè)方法要找到指定的布局文件,進(jìn)行不斷地解析每個(gè)節(jié)點(diǎn):從最頂端的節(jié)點(diǎn)進(jìn)行一層一層的解析查詢(xún)钧栖,找到后在一層一層的返回低零,如果在左邊沒(méi)找到,就會(huì)接著解析右邊拯杠,并進(jìn)行相應(yīng)的查詢(xún)掏婶,直到找到位置。特點(diǎn):xml文件被解析的時(shí)候潭陪,只要被創(chuàng)建出來(lái)了气堕,其孩子的id就不會(huì)改變了。根據(jù)這個(gè)特點(diǎn)畔咧,可以將孩子id存入到指定的集合中,每次就可以直接取出集合中對(duì)應(yīng)的元素就可以了)揖膜。 而RecyclerView是強(qiáng)制存在規(guī)定好的ViewHolder誓沸。
??????4)綁定事件的方式不同,ListView是在主方法中ListView對(duì)象的setOnItemClickListener方法壹粟;RecyclerView則是在子項(xiàng)具體的View中去注冊(cè)事件拜隧。
??????5) 緩存方式有區(qū)別:RecyclerView中mCacheViews(屏幕外)獲取緩存時(shí),是通過(guò)匹配pos獲取目標(biāo)位置的緩存趁仙,這樣做的好處是洪添,當(dāng)數(shù)據(jù)源數(shù)據(jù)不變的情況下,可以做到屏幕外的列表項(xiàng)ItemView進(jìn)入屏幕內(nèi)時(shí)也無(wú)須bindView快速重用雀费;ListView緩存View干奢,同樣是離屏緩存,ListView從mScrapViews根據(jù)pos獲取相應(yīng)的緩存盏袄,但是并沒(méi)有直接使用忿峻,而是重新getView(即必定會(huì)重新bindView)薄啥。
??????6) 刷新方式:RecyclerView更大的亮點(diǎn)在于提供了局部刷新的接口,通過(guò)局部刷新逛尚,就能避免調(diào)用許多無(wú)用的bindView垄惧;ListView和RecyclerView最大的區(qū)別在于數(shù)據(jù)源改變時(shí)的緩存的處理邏輯,ListView是"一鍋端"绰寞,將所有的mActiveViews都移入了二級(jí)緩存mScrapViews到逊,而RecyclerView則是更加靈活地對(duì)每個(gè)View修改標(biāo)志位,區(qū)分是否重新bindView滤钱。