RecyclerView的回收被封装在内部类Recycler中 从这个类的成员变量就能略窥一斑
个人总结:
mChangedScrap: RecyclerView中需要改变的vh
mAttachedScrap: RecyclerView还没有分离的vh
mCacheViews: RecyclerView中vh缓存
RecycledViewPool: 可共享回收池
/**
* A Recycler is responsible for managing scrapped or detached item views for reuse.
*
* <p>A "scrapped" view is a view that is still attached to its parent RecyclerView but
* that has been marked for removal or reuse.</p>
*
* <p>Typical use of a Recycler by a {@link LayoutManager} will be to obtain views for
* an adapter's data set representing the data at a given position or item ID.
* If the view to be reused is considered "dirty" the adapter will be asked to rebind it.
* If not, the view can be quickly reused by the LayoutManager with no further work.
* Clean views that have not {@link android.view.View#isLayoutRequested() requested layout}
* may be repositioned by a LayoutManager without remeasurement.</p>
*/
public final class Recycler {
//如果仍依赖于 RecyclerView (比如已经滑动出可视范围,但还没有被移除掉),但已经被标记移除的 ItemView 集合会被添加到 mAttachedScrap 中。
final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
//mChangedScrap存储 notifXXX 方法时需要改变的 ViewHolder 。
ArrayList<ViewHolder> mChangedScrap = null;
//如果 mAttachedScrap 中不再依赖时会被加入到 mCachedViews 中。
final ArrayList<ViewHolder> mCachedViews = new ArrayList<ViewHolder>();
private final List<ViewHolder>
mUnmodifiableAttachedScrap = Collections.unmodifiableList(mAttachedScrap);
private int mRequestedCacheMax = DEFAULT_CACHE_SIZE;
int mViewCacheMax = DEFAULT_CACHE_SIZE;
//被remove掉的ViewHolder会按照ViewType分组被存放在RecyclerViewPool里,默认最大缓存每组(ViewType)5个。
private RecycledViewPool mRecyclerPool;
private ViewCacheExtension mViewCacheExtension;
static final int DEFAULT_CACHE_SIZE = 2;
这个类的最重要的入口即
getViewForPosition方法,
可见的外部调用如下:
GridLayoutManager.getViewForPosition in GridLayoutManager.java (android\support\v17\leanback\widget) :
return mRecycler.getViewForPosition(position);
GridLayoutManager.measureScrapChild in GridLayoutManager.java (android\support\v17\leanback\widget) :
GridLayoutManager.measureScrapChild in GridLayoutManager.java (android\support\v17\leanback\widget) :
View view = mRecycler.getViewForPosition(position);
LayoutState.next in LayoutState.java (android\support\v7\widget) :
LayoutState.next in LayoutState.java (android\support\v7\widget) :
final View view = recycler.getViewForPosition(mCurrentPosition);
LinearLayoutManager.LayoutState.next in LinearLayoutManager.java (android\support\v7\widget) :
LinearLayoutManager.LayoutState.next in LinearLayoutManager.java (android\support\v7\widget) :
final View view = recycler.getViewForPosition(mCurrentPosition);
RecyclerView.Recycler.prefetch in RecyclerView.java (android\support\v7\widget) :
RecyclerView.Recycler.prefetch in RecyclerView.java (android\support\v7\widget) :
prefetchView = getViewForPosition(childPosition);
在LinearLayoutManager、GridLayoutManager中都是调用这个方法
进入源码,可以看到这个方法是一个重载方法
/**
* Obtain a view initialized for the given position.
*
* This method should be used by {@link LayoutManager} implementations to obtain
* views to represent data from an {@link Adapter}.
* <p>
* The Recycler may reuse a scrap or detached view from a shared pool if one is
* available for the correct view type. If the adapter has not indicated that the
* data at the given position has changed, the Recycler will attempt to hand back
* a scrap view that was previously initialized for that data without rebinding.
*
* @param position Position to obtain a view for
* @return A view representing the data at <code>position</code> from <code>adapter</code>
*/
public View getViewForPosition(int position) {
return getViewForPosition(position, false);
}
View getViewForPosition(int position, boolean dryRun) {
私有方法中,第二个参数是个boolean值(实弹执行? 传入true目测是内部单元测试用的?) public方法则是默认传入false的重载
以下分析默认该值为false
View getViewForPosition(int position, boolean dryRun) {
if (position < 0 || position >= mState.getItemCount()) {
throw new IndexOutOfBoundsException("Invalid item position " + position
+ "(" + position + "). Item count:" + mState.getItemCount());
}
boolean fromScrap = false;
ViewHolder holder = null;
入口处是越界判断
接下来进入第1个缓存内获取
如果preLayout? 那么尝试从changed scrap里查找
// 0) If there is a changed scrap, try to find from there
if (mState.isPreLayout()) {
holder = getChangedScrapViewForPosition(position);
fromScrap = holder != null;
}
来看看getChangedScapViewForPosition的实现: