RecyclerView,看名字就知道,这玩意儿的精髓在于回收/复用。
- 复用了啥?
View - 哪里会触发回收/复用?
- MOVE事件
a. onTouchEvent
b. MotionEvent.ACTION_MOVE
c. scrollByInternal
d. scrollStep
e. LayoutManager的scrollHorizontallyBy/scrollVerticallyBy
- 这里看LinearLayoutManager中具体实现
a. scrollBy
b. fill
c. layoutChunk这个方法是在一个while循环里调用的
d. View view = layoutState.next(recycler); 获取View
e. addView(view); 使用View
f. layoutState.next这里重点跟这个方法
g. recycler.getViewForPosition
这里已经找到了回收/复用关键Recycler类
a. recycler.getViewForPosition
b. tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView
c. 上面是那个ViewHolder之后直接拿itemView,这样问题就转变成了怎么拿ViewHolder
d. tryGetViewHolderForPositionByDeadline - notify数据变化
- MOVE事件
- 怎么回收/复用?
Recycler类:
下面几个数据结构用来缓存回收的ViewHolder。
a. mAttachedScrap 和 mChangedScrap 用来缓存屏幕内的 ViewHolderfinal ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>(); ArrayList<ViewHolder> mChangedScrap = null; final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>(); RecycledViewPool mRecyclerPool; private ViewCacheExtension mViewCacheExtension;
- 其中mChangedScrap主要服务于动画
b. mCachedViews 用来缓存屏幕外的 ViewHolder
- 默认缓存2个
- 通过setViewCacheSize可改变缓存容量
c. mViewCacheExtension 用户自定义缓存机制
d. mRecyclerPool 用来缓存屏幕外的 ViewHolder
- 从mCachedViews中淘汰的ViewHolder会被缓存到 mRecyclerPool 中
- mRecyclerPool 是根据 type 来获取 ViewHolder,每个 type 默认最大缓存 5 个。可通过setMaxRecycledViews(int viewType, int max) 设置自定义大小。
- RecycledViewPool lets you share Views between multiple RecyclerViews.
- 也就是说,当多个RecyclerView使用相同的ViewHolder时,可以共享缓存。
- ViewHolder 在被缓存到 RecycledViewPool 时,会将内部的数据清理,因此从 RecycledViewPool 中取出来的 ViewHolder 需要重新调用 onBindViewHolder 绑定数据
#回收:
RecyclerView - onLayout
RecyclerView - dispatchLayout
RecyclerView - dispatchLayoutStep2();
mLayout.onLayoutChildren(mRecycler, mState);
LinearLayoutManager - onLayoutChildren
LinearLayoutManager - detachAndScrapAttachedViews
LinearLayoutManager - scrapOrRecycleView
```
if (viewHolder.isInvalid() && !viewHolder.isRemoved()
&& !mRecyclerView.mAdapter.hasStableIds()) {
removeViewAt(index);
recycler.recycleViewHolderInternal(viewHolder);
} else {
detachViewAt(index);
recycler.scrapView(view);
mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
}
```
- recycler.recycleViewHolderInternal(viewHolder)
- recycler.scrapView(view);
兜兜转转,最后终于找到Recycler了
###recycleViewHolderInternal
处理屏幕外缓存
###scrapView
处理屏幕内缓存
#复用:
流程都在 tryGetViewHolderForPositionByDeadline 中
###大体流程:
a. 从屏幕内缓存找
b. 从mCachedViews缓存找
c. 从mViewCacheExtension缓存找
d. 从mRecyclerPool找
e. 找不到,调用adapter的create方法
###代码流程:
a. getChangedScrapViewForPosition
- If there is a changed scrap, try to find from there
- 从 mChangedScrap 找
b. getScrapOrHiddenOrCachedHolderForPosition
- Find by position from scrap/hidden list/cache
- 从 mAttachedScrap和mCachedViews找
- 这里是根据Position
c. getScrapOrCachedViewForId
- Find from scrap/cache via stable ids, if exists
- 依然通过 mAttachedScrap和mCachedViews找
- 这里是根据ID
d. mViewCacheExtension.getViewForPositionAndType(this, position, type);
- We are NOT sending the offsetPosition because LayoutManager does not know it.
- 从mViewCacheExtension中找
e. holder = getRecycledViewPool().getRecycledView(type);
- 从mRecyclerPool中找
f. holder = mAdapter.createViewHolder(RecyclerView.this, type);
- 直接通过Adapter的createViewHolder的创建
g. tryBindViewHolderByDeadline
- 绑定数据
- mAdapter.bindViewHolder(holder, offsetPosition);