本文有分析错误的地方,recyclerView真复杂.我也是把自己大概知道的地方整理了一下,没有形成系统的,有明确定义的文章.仅供参考.
RecyclerView setAdapter()的过程
public class RecyclerView extends ViewGroup {
private static final String TAG = "RecyclerView";
// ..............省略代码
public void setAdapter(Adapter adapter) {
setAdapterInternal(adapter, false, true);
requestLayout();
}
private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {
if (mAdapter != null) {
mAdapter.unregisterAdapterDataObserver(mObserver);
}
if (!compatibleWithPrevious || removeAndRecycleViews) {
if (mItemAnimator != null) {
mItemAnimator.endAnimations();
}
if (mLayout != null) {
mLayout.removeAndRecycleAllViews(mRecycler);
mLayout.removeAndRecycleScrapInt(mRecycler, true);
}
}
mAdapterHelper.reset();
final Adapter oldAdapter = mAdapter;
mAdapter = adapter;
if (adapter != null) {
//注册内容观察者
adapter.registerAdapterDataObserver(mObserver);
}
if (mLayout != null) {
mLayout.onAdapterChanged(oldAdapter, mAdapter);
}
mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
//设置结构发生改变的标志位
mState.mStructureChanged = true;
// 刷新视图
markKnownViewsInvalid();
}
//省略代码 ............
}
//观察者处理布局数据
private class RecyclerViewDataObserver extends AdapterDataObserver {
@Override
public void onChanged() {
assertNotInLayoutOrScroll(null);
if (mAdapter.hasStableIds()) {
mState.mStructureChanged = true;
mDataSetHasChangedAfterLayout = true;
} else {
mState.mStructureChanged = true;
mDataSetHasChangedAfterLayout = true;
}
//需要重新布局
if (!mAdapterHelper.hasPendingUpdates()) {
requestLayout();
}
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
assertNotInLayoutOrScroll(null);
if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount)) {
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 (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) {
ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
} else {
mAdapterUpdateDuringMeasure = true;
requestLayout();
}
}
}
我们看到从setAdapter()之后,会调用到RecyclerViewDataObserver 的onChanged 函数,在该函数中又会调用RecyclerView 的requestLayout函数进行重新布局.而RecyclerView把布局的任务交给了LayoutManager.
RecyclerView测量阶段
1.在dispatchLayoutStep1()
主要做了进程适配器更新;决定应该运行哪个动画;保存当前视图的信息;如有必要运行预测布局并保存其信息(数据变化前的更新).
2. dispatchLayoutStep2()
实际布局的方法,会把过程交给LayoutManager里面的onLayoutChildren()方法.
3. dispatchLayoutStep3()
布局的最后一步,我们保存有关动画视图的信息,触发动画并进行任何必要的清理.
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
//没有 LayoutManager的时候,设置默认的测量模式.就是那些EXACTLY,AT_MOST,UNSPECIFIED
if (mLayout == null) {
defaultOnMeasure(widthSpec, heightSpec);
return;
}
//有 LayoutManager,而且开启自动测量,这个时候就可以为 RecyclerView 设置 wrap_content 值了
if (mLayout.mAutoMeasure) {
final int widthMode = MeasureSpec.getMode(widthSpec);
final int heightMode = MeasureSpec.getMode(heightSpec);
final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
&& heightMode == MeasureSpec.EXACTLY;
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
if (skipMeasure || mAdapter == null) {
return;
}
if (mState.mLayoutStep == State.STEP_START) {
//调用LayoutManager的方法
dispatchLayoutStep1();
}
// set dimensions in 2nd step. Pre-layout should happen with old dimensions for
// consistency
mLayout.setMeasureSpecs(widthSpec, heightSpec);
mState.mIsMeasuring = true;
//调用LayoutManager的方法
dispatchLayoutStep2();
// 现在我们可以从子view那里获得宽度和高度。
mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
if (mLayout.shouldMeasureTwice()) {
mLayout.setMeasureSpecs(
MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));
mState.mIsMeasuring = true;
dispatchLayoutStep2();
// now we can get the width and height from the children.
mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
}
} else {
//若 RecyclerView 已经设置了固定的值,那就执行 LayoutManager 的 onMeasure() 方法
//设置方法setAutoMeasureEnable(false)
if (mHasFixedSize) {
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
return;
}
// custom onMeasure
if (mAdapterUpdateDuringMeasure) {
eatRequestLayout();
onEnterLayoutOrScroll();
processAdapterUpdatesAndSetAnimationFlags();
onExitLayoutOrScroll();
if (mState.mRunPredictiveAnimations) {
mState.mInPreLayout = true;
} else {
// consume remaining updates to provide a consistent state with the layout pass.
mAdapterHelper.consumeUpdatesInOnePass();
mState.mInPreLayout = false;
}
mAdapterUpdateDuringMeasure = false;
resumeRequestLayout(false);
} else if (mState.mRunPredictiveAnimations) {
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
return;
}
if (mAdapter != null) {
mState.mItemCount = mAdapter.getItemCount();
} else {
mState.mItemCount = 0;
}
eatRequestLayout();
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
resumeRequestLayout(false);
mState.mInPreLayout = false; // clear
}
}
//-------------------------------------------------------------------------------------------------------------------------
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
eatRequestLayout();
//分发layout
dispatchLayout();
resumeRequestLayout(false);
mFirstLayoutComplete = true;
}
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();
}
在onLayout()与onMeasure()方法中我们大概可以得知:
1.在onMeasure()过程中,会判断是否支持WRAP_CONTENT,如果支持那么就会提前调用dispatchLayoutStep1,2()方法,先确定子控件的大小和位置,再设置recyclerView的大小.
2.如果不是这种情况那么就会调用onLayout()方法去执行.RecyclerView将布局childView的操作交给了LayoutManager,在布局过程中,也有很多关于动画的处理,在 dispatchLayoutStep1,2,3()这几个过程中均有体现.因为太过复杂,这里没有介绍.
ViewHolder存入缓存的过程
在使用LayoutManager这个类的时候会用到的缓存的概念.实际上我们使用recyclerView的时候也必须设置布局管理器.
一般的我们自定义一个LayoutManager的时候,大致需要如下的模板来完成.
//一般的自定义LayoutManager模板代码
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler,
RecyclerView.State state) {
//Scrap measure one child
//根据Adapter的位置position找到RecyclerView中对应的item
View scrap = recycler.getViewForPosition(0);
addView(scrap);
measureChildWithMargins(scrap, 0, 0);
//获取childView在所占的空间
mDecoratedChildWidth = getDecoratedMeasuredWidth(scrap);
mDecoratedChildHeight = getDecoratedMeasuredHeight(scrap);
//标记detach要回收的view
detachAndScrapView(scrap, recycler);
updateWindowSizing();
int childLeft;
int childTop;
/*
* Reset the visible and scroll positions
*/
mFirstVisiblePosition = 0;
childLeft = childTop = 0;
//将所有子view交给recycler去执行缓存策略
detachAndScrapAttachedViews(recycler);
//Fill the grid for the initial layout of views
fillGrid(DIRECTION_NONE, childLeft, childTop, recycler);
}
在上面提到的dispatchLayoutStep1.2这两个方法中都会调用到onLayoutChildren,然后调用detachAndScrapAttachedViews的关键方法.下面是源码:
//遍历子View 加入缓存
public void detachAndScrapAttachedViews(Recycler recycler) {
final int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
final View v = getChildAt(i);
scrapOrRecycleView(recycler, i, v);
}
}
//判断子View该怎么加入缓存
private void scrapOrRecycleView(Recycler recycler, int index, View view) {
final ViewHolder viewHolder = getChildViewHolderInt(view);
if (viewHolder.shouldIgnore()) {
if (DEBUG) {
Log.d(TAG, "ignoring view " + viewHolder);
}
return;
}
//如果不是setAdapter或者整体刷新,不是调用了remove方法或者View绑定内容无效
//则移除VIew放入到 mCachedViews,mRecyclerPool中
////stable id 标识一个viewholder的唯一性
if (viewHolder.isInvalid() && !viewHolder.isRemoved()
&& !mRecyclerView.mAdapter.hasStableIds()) {
removeViewAt(index);
recycler.recycleViewHolderInternal(viewHolder);
} else {
//否则加入 mAttachedScrap,mChangedScrap缓存中
detachViewAt(index);
recycler.scrapView(view);
mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
}
}
从上面的那个方法我们看到了Recycler根据不同的条件来加入不同的缓存集合中.接着具体的看一下,在scrapOrRecycleView方法中的removeViewAt(index) 之后是怎么把ViewHolder放入对应的缓冲池里面.
//viewHolder存放缓存策略
void recycleViewHolderInternal(ViewHolder holder) {
//省略抛出异常
//noinspection unchecked
final boolean transientStatePreventsRecycling = holder
.doesTransientStatePreventRecycling();
final boolean forceRecycle = mAdapter != null
&& transientStatePreventsRecycling
&& mAdapter.onFailedToRecycleView(holder);
boolean cached = false;
boolean recycled = false;
if (DEBUG && mCachedViews.contains(holder)) {
throw new IllegalArgumentException("cached view received recycle internal? "
+ holder + exceptionLabel());
}
if (forceRecycle || holder.isRecyclable()) {
if (mViewCacheMax > 0
&& !holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_REMOVED
| ViewHolder.FLAG_UPDATE
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)) {
//
int cachedViewSize = mCachedViews.size();
//如果大于mViewCacheMax = 2
if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
//将cachedViewIndex为0的数据存入pool
recycleCachedViewAt(0);
cachedViewSize--;
}
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;
if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
break;
}
cacheIndex--;
}
targetCacheIndex = cacheIndex + 1;
}
//将holder加入缓存
mCachedViews.add(targetCacheIndex, holder);
cached = true;
}
//如果缓存cached失败就加入pool.失败的条件是 holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID
// | ViewHolder.FLAG_REMOVED
// | ViewHolder.FLAG_UPDATE
// | ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN)
if (!cached) {
addViewHolderToRecycledViewPool(holder, true);
recycled = true;
}
} else {
// NOTE: 在动画运行时滚动视图时,视图无法回收。 In this case, the item is eventually recycled by
// ItemAnimatorRestoreListener#onAnimationFinished.
// TODO: consider cancelling an animation when an item is removed scrollBy,
// to return it to the pool faster
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());
}
}
// even if the holder is not removed, we still call this method so that it is removed
// from view holder lists.
//即使没有删除holder,我们仍然会调用此方法,以便将其从view holder列表中删除。
mViewInfoStore.removeViewHolder(holder);
if (!cached && !recycled && transientStatePreventsRecycling) {
holder.mOwnerRecyclerView = null;
}
}
// 将index为0的holder加入pool中,然后从cached中移除这个0
void recycleCachedViewAt(int cachedViewIndex) {
if (DEBUG) {
Log.d(TAG, "Recycling cached view at index " + cachedViewIndex);
}
ViewHolder viewHolder = mCachedViews.get(cachedViewIndex);
if (DEBUG) {
Log.d(TAG, "CachedViewHolder to be recycled: " + viewHolder);
}
addViewHolderToRecycledViewPool(viewHolder);
mCachedViews.remove(cachedViewIndex);
}
我们从recycleViewHolderInternal方法可以得出以下结论:首先确认mCacheViews集合中是否还有空位,如果有空位,则直接放到mCacheViews集合,如果没有的话就把mCacheViews集合中最前面的ViewHolder拿出来放到RecyclerViewPool中,然后再把最新的这个ViewHolder放到mCacheViews集合,如果没有成功缓存到mCacheViews集合中,就直接放入RecyclerViewPool中.
接着看detachViewAt(index) 之后做了什么.
/**
* 将依附的视图标记为剥离的状态
*
* “Scrap”视图依然附加在父级RecyclerView,可以重新绑定与重用。
* @param view View to scrap
*/
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);
}
}
用一个例子来说明一下(例子来自揪克)我在这个大神的demo上变动了一些UI.
- 在mAttachedScrap会把屏幕上展示的内容缓存下来.mCachedViews默认大小为2.
ViewHolder取出缓存的过程
在LinearLayoutManager里面的fill方法的layoutChunk() 中我们看到了取出缓存的过程.
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
...
layoutChunk(recycler, state, layoutState, layoutChunkResult);
...
}
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
View view = layoutState.next(recycler);
...
}
View next(RecyclerView.Recycler recycler) {
if (mScrapList != null) {
return nextViewFromScrapList();
}
//取出缓存里面的view
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
}
进入到RecyclerView里面的方法:
@Nullable
ViewHolder tryGetViewHolderForPositionByDeadline(int position,
boolean dryRun, long deadlineNs) {
if (position < 0 || position >= mState.getItemCount()) {
throw new IndexOutOfBoundsException("Invalid item position " + position
+ "(" + position + "). Item count:" + mState.getItemCount()
+ exceptionLabel());
}
boolean fromScrapOrHiddenOrCache = false;
ViewHolder holder = null;
// 0) If there is a changed scrap, try to find from there
//预布局之前,动画相关
if (mState.isPreLayout()) {
//从mChangedScrap取出holder
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
// 1) Find by position from scrap/hidden list/cache
//1) 根据位置到scrap/hidden list/cache找
if (holder == null) {
holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
if (holder != null) {
//position如果匹配到了 ViewHolder,还判断这个 ViewHolder 是否已经被 remove 与type的类型
if (!validateViewHolderForOffsetPosition(holder)) {
// recycle holder (and unscrap if relevant) since it can't be used
if (!dryRun) {
// we would like to recycle this but need to make sure it is not used by
// animation logic etc.
holder.addFlags(ViewHolder.FLAG_INVALID);
//如果recycler不是空的,那么就是这个viewholder加入过了scrap中
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);
if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
+ "position " + position + "(offset:" + offsetPosition + ")."
+ "state:" + mState.getItemCount() + exceptionLabel());
}
final int type = mAdapter.getItemViewType(offsetPosition);
// 2) Find from scrap/cache via stable ids, if exists
//通过id查找
if (mAdapter.hasStableIds()) {
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),
type, dryRun);
if (holder != null) {
// update position更新位置
holder.mPosition = offsetPosition;
fromScrapOrHiddenOrCache = true;
}
}
//需要用户自定义的缓存策略
if (holder == null && mViewCacheExtension != null) {
// We are NOT sending the offsetPosition because LayoutManager does not
// know it.
final View view = mViewCacheExtension
.getViewForPositionAndType(this, position, type);
if (view != null) {
holder = getChildViewHolder(view);
if (holder == null) {
throw new IllegalArgumentException("getViewForPositionAndType returned"
+ " a view which does not have a ViewHolder"
+ exceptionLabel());
} else if (holder.shouldIgnore()) {
throw new IllegalArgumentException("getViewForPositionAndType returned"
+ " a view that is ignored. You must call stopIgnoring before"
+ " returning this view." + exceptionLabel());
}
}
}
//通过缓存池里面去取出
if (holder == null) { // fallback to pool
if (DEBUG) {
Log.d(TAG, "tryGetViewHolderForPositionByDeadline("
+ position + ") fetching from shared pool");
}
//这个type就是item的种类
holder = getRecycledViewPool().getRecycledView(type);
//找到了viewholder
if (holder != null) {
//重置内部的状态,这样就可以重新复用了
holder.resetInternal();
if (FORCE_INVALIDATE_DISPLAY_LIST) {
invalidateDisplayListInt(holder);
}
}
}
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;
}
//--------------------如果pool里面也没有找到,那就重新创建ViewHolder---------------------
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");
}
}
}
// This is very ugly but the only place we can grab this information
// before the View is rebound and returned to the LayoutManager for post layout ops.
// We don't need this in pre-layout since the VH is not updated by the LM.
if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder
.hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
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());
recordAnimationInfoIfBouncedHiddenView(holder, info);
}
}
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);
//------------------------------------重新绑定viewholder------------------------------------------------
bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
}
//设置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;
}
从这个动图来看,mCacheViews也印证了(recycleViewHolderInternal方法)上面说的存储方式.在上下滑动的时候,会匹配id,直接取缓存,这样就只会执行bindViewHolder了.
RecycledViewPool
public static class RecycledViewPool {
//viewType的默认缓存数量为5.
private static final int DEFAULT_MAX_SCRAP = 5;
static class ScrapData {
ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
int mMaxScrap = DEFAULT_MAX_SCRAP;
long mCreateRunningAverageNs = 0;
long mBindRunningAverageNs = 0;
}
SparseArray<ScrapData> mScrap = new SparseArray<>();
//计数器
private int mAttachCount = 0;
public void clear() {
for (int i = 0; i < mScrap.size(); i++) {
ScrapData data = mScrap.valueAt(i);
data.mScrapHeap.clear();
}
}
//设置缓存数量的方法
public void setMaxRecycledViews(int viewType, int max) {
ScrapData scrapData = getScrapDataForType(viewType);
scrapData.mMaxScrap = max;
final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
if (scrapHeap != null) {
//如果超过最大值,会remove掉相应ViewHolder。
while (scrapHeap.size() > max) {
scrapHeap.remove(scrapHeap.size() - 1);
}
}
}
public int getRecycledViewCount(int viewType) {
return getScrapDataForType(viewType).mScrapHeap.size();
}
public ViewHolder getRecycledView(int viewType) {
final ScrapData scrapData = mScrap.get(viewType);
if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
//从缓存list的尾部逐个取出ViewHolder
return scrapHeap.remove(scrapHeap.size() - 1);
}
return null;
}
int size() {
int count = 0;
for (int i = 0; i < mScrap.size(); i++) {
ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
if (viewHolders != null) {
count += viewHolders.size();
}
}
return count;
}
//
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;
}
if (DEBUG && scrapHeap.contains(scrap)) {
throw new IllegalArgumentException("this scrap item already exists");
}
//重置缓存viewholder的属性
scrap.resetInternal();
//加入缓存中
scrapHeap.add(scrap);
}
//省略代码......
//RecyclerViewPool与一个adapter绑定会+1
void attach(Adapter adapter) {
mAttachCount++;
}
//RecyclerViewPool与一个adapter解绑会-1
void detach() {
mAttachCount--;
}
//绑定与解绑会回调此方法,同时计数
void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,
boolean compatibleWithPrevious) {
if (oldAdapter != null) {
detach();
}
if (!compatibleWithPrevious && mAttachCount == 0) {
//0的时候,就会清空这个RecyclerViewPool缓存池里的所有ViewHolder
clear();
}
if (newAdapter != null) {
attach(newAdapter);
}
}
private ScrapData getScrapDataForType(int viewType) {
ScrapData scrapData = mScrap.get(viewType);
if (scrapData == null) {
scrapData = new ScrapData();
mScrap.put(viewType, scrapData);
}
return scrapData;
}
}
下面主要展示了缓存池内部的状态.保存的viewHolder类型,以及在多type的item情况下,createViewHolder与BindVIewHolder
的显示.
关于缓存池我并没有看懂系统是怎么样的策略来把mCachedView里面viewHolder赋值到RecycledViewPool中的.
目前我只是知道缓存池是用来处理不同itemViewType的缓存.避免多类型item重复创建对象.