RecyclerView

2020-07-02  本文已影响0人  竹叶儿青

1. 前置知识

2. 复用

复用的是ViewHolder,关键部分在RecyclerView内部类Recycler的 tryGetViewHolderForPositionByDeadline方法。
看一下Recycler类的几个关键成员变量:

 public final class Recycler {
        final ArrayList<ViewHolder> mAttachedScrap = new ArrayList<>();
        ArrayList<ViewHolder> mChangedScrap = null;

        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;

        RecycledViewPool mRecyclerPool;

        private ViewCacheExtension mViewCacheExtension;

        static final int DEFAULT_CACHE_SIZE = 2;

关键方法:

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) {
                    // We are NOT sending the offsetPosition because LayoutManager does not
                    // know it.
                    final View view = mViewCacheExtension
                            .getViewForPositionAndType(this, position, type);
                    ...
                   if (holder == null) { // fallback to pool
                    holder = getRecycledViewPool().getRecycledView(type);
                     ...
                   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;
                    }
                    holder = mAdapter.createViewHolder(RecyclerView.this, type);

总的来说,复用有6层:

  1. If there is a changed scrap, try to find from there
    从changedScrap获取
  2. Find by position from scrap/hidden list/cache
    根据position从scrap/hidden list/cache获取,用到mAttachedScrap、mCachedViews
  3. Find from scrap/cache via stable ids, if exists
    根据stable id从scrap/cache获取,用到mAttachedScrap、mCachedViews
  4. 从mViewCacheExtension获取,这个是用户自定义的
  5. fallback to pool
    从RecycledViewPool中获取
  6. 调用mAdapter.createViewHolder(RecyclerView.this, type)创建ViewHolder

3. 回收

回收的同样是ViewHolder。
关键在LayoutManager的removeAndRecycleViewAt方法:

public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) {
            final View view = getChildAt(index);
            removeViewAt(index);
            recycler.recycleView(view);
        }

  public void recycleView(@NonNull View view) {
            // This public recycle method tries to make view recycle-able since layout manager
            // intended to recycle this view (e.g. even if it is in scrap or change cache)
            ViewHolder holder = getChildViewHolderInt(view);
            if (holder.isTmpDetached()) {
                removeDetachedView(view, false);
            }
            if (holder.isScrap()) {
                holder.unScrap();
            } else if (holder.wasReturnedFromScrap()) {
                holder.clearReturnedFromScrapFlag();
            }
            recycleViewHolderInternal(holder);

 void recycleViewHolderInternal(ViewHolder holder) {
  ...
 int cachedViewSize = mCachedViews.size();
                    if (cachedViewSize >= mViewCacheMax && cachedViewSize > 0) {
                        recycleCachedViewAt(0);
                        cachedViewSize--;
                    }
  ...
 mCachedViews.add(targetCacheIndex, holder);

 if (!cached) {
          addViewHolderToRecycledViewPool(holder, true);
          recycled = true;
                }

总的来说,回收有以下3步:

  1. 如果mCachedViews的size超过了mViewCacheMax,则将最久远的viewholder,即position为0的,放入RecycledViewPool中;
  2. 将当前viewHolder插入mCachedViews的适当位置,放在最近使用过的view后面;
  3. 如果没有缓存,则直接加入RecycledViewPool中。
上一篇下一篇

猜你喜欢

热点阅读