写缓存前先记录复用:(先缓存再复用)
//上下滑动时进入的方法。fill 在分析复用和缓存时都比较重要
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable)
//复用时,取出 ViewHolder 的主要方法
layoutChunk(recycler, state, layoutState, layoutChunkResult);
列表的复用也就是对于 ViewHolder 的复用
//滑动列表时, item 的复用会走到此方法
tryGetViewHolderForPositionByDeadline()
第一个复用:
//复用第一个判断 changed scrap, 此 mState.isPreLayout() 判断对于动画而言的
// 0) If there is a changed scrap, try to find from there
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrapOrHiddenOrCache = holder != null;
}
//进入此方法 也就是 ChangedScrap 的复用
--> getChangedScrapViewForPosition()
||
||
// 第一个重点 mChangedScrap ,获取可以复用的ViewHolder
--> ViewHolder holder = mChangedScrap.get(i);
// 第二个重点 hasStableIds(), 这是对于item设置的id, 当需要对每个item单独刷新时可设置此值,保证在刷新降低item闪动
--> mAdapter.hasStableIds()
第二个复用
// 如果第一个复用 没有找到 ViewHolder 值
if (holder == null){
// 通过这个获取 ViewHolder,需要的是 postion
--> getScrapOrHiddenOrCachedHolderForPosition(postion)
}
//第二个复用的主要方法
--> getScrapOrHiddenOrCachedHolderForPosition(postion)
// 重点-: mAttachedScrap 先通过次 属性获取ViewHolder
--> ViewHolder holder = mAttachedScrap.get(i);
// 重点二:找隐藏的没有移出 的item, 取出view
--> View view = mChildHelper.findHiddenNonRemovedView(position);
// 重点三:从 mCachedViews 中取出可用的ViewHolder
--> ViewHolder holder = mCachedViews.get(i);
第三个复用
// 对于有动画的item 而言; 当第二个复用依然没有获取到ViewHolder对象的时候,就利用 stableId 获取
holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition)
// 重点一: mAttachedScrap 获取ViewHolder
--> ViewHolder holder = mAttachedScrap.get(i);
// 重点二: mCachedViews 获取ViewHolder
--> ViewHolder holder = mCachedViews.get(i);
第四个复用 (自定义实现)
// 对自定义 复用 ViewHolder; 对于缓存的话 也需要自己实现
View view = mViewCacheExtension.getViewForPositionAndType(this, position, type);
第五个复用 (缓存池)
// 从 RecycledViewPool 中 获取 ScrapData 对象,再调用 scrapData.mScrapHeap;获取ViewHolder 的 list 数据
holder = getRecycledViewPool().getRecycledView(type);
第六步: 如果上面都没有找到能够使用ViewHolder,将会调用
//直接创建一个新的
holder = mAdapter.createViewHolder(RecyclerView.this, type);
onBindViewHolder 在什么时候调用?
//经历了以上 6步后ViewHolder一定存在对象,对ViewHolder 里面的控件对象赋值时,需要判断是否进入屏幕内
f (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
// 重点: 此方法是 mAdapter.bindViewHolder(holder, offsetPosition);调用的时机
--> bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs);
}
那么复用在缓存后, 缓存是什么时候进行的呢?(回到fill方法,缓存在复用之前, 复用的是layoutChunk那么缓存recycleByLayoutState就是此方法)
//LineaLayoutManager 中的 fill()方法为开始
fill()
//开始缓存
--> recycleByLayoutState(recycler, layoutState);
//recycleViewsFromEnd 和 recycleViewsFromStart 缓存时都调用了recycleChildren() 方法
--> recycleViewsFromEnd(recycler, scrollingOffset, noRecycleSpace);
--> recycler.recycleView(view)
// 接收 ViewHolder, 然后进行
--> recycleViewHolderInternal(ViewHolder holder)
--> mCachedViews 的缓存
//1: 第一:先判断 mCachedViews的缓存数,cachedViewSize >= mViewCacheMax && cachedViewSize > 0(mViewCacheMax = 2), 如果满足条件将移出CacheView 里面的 第0 条ViewHolder
-->recycleCachedViewAt(0);
//第二: 将移出的 ViewHolder, 添加到 RecycledViewPool 里面
--> addViewHolderToRecycledViewPool(viewHolder, true);
// 第三: 此方法 mScrap.get(viewType).mMaxScrap <= scrapHeap.size() (注:mMaxScrap = 5) 判断Pool 是否还能添加, 如果不能直接 return; 而且 pool 保存在
--> getRecycledViewPool().putRecycledView(holder);
//2: RecycledViewPool 缓存)
--> Pool 的缓存
-->addViewHolderToRecycledViewPool(holder, true);
注意:
ArrayList<ViewHolder> mCachedViews 能缓存的长度 是 DEFAULT_CACHE_SIZE = 2
ArrayList<ViewHolder> scrapHeap pool 能缓存的长度 是 DEFAULT_MAX_SCRAP = 5