RecyclerView複用回收機制

Recycler組成

RecyclerView的緩存主要委託給了Recycler,先了解下組成

public final class Recycler {
    final ArrayList<RecyclerView.ViewHolder> mAttachedScrap = new ArrayList();
    ArrayList<RecyclerView.ViewHolder> mChangedScrap = null;
    final ArrayList<RecyclerView.ViewHolder> mCachedViews = new ArrayList();
    private final List<RecyclerView.ViewHolder> mUnmodifiableAttachedScrap;
    private int mRequestedCacheMax;
    int mViewCacheMax;
    RecyclerView.RecycledViewPool mRecyclerPool;
    private RecyclerView.ViewCacheExtension mViewCacheExtension;
    static final int DEFAULT_CACHE_SIZE = 2;
    //...
    }
    * 1.一級緩存:mAttachedScrap
    * 2.二級緩存:mCacheViews
    * 3.三級緩存:mViewCacheExtension
    * 4.四級緩存:mRecyclerPool

在RecyclerView的繪製流程中分析過,layoutManager會在layoutChunk方法中對view進行獲取添加測量等操作,如下所示

//LinerLayoutManager.java
void layoutChunk(Recycler recycler, State state, LinearLayoutManager.LayoutState layoutState, LinearLayoutManager.LayoutChunkResult result) {
     View view = layoutState.next(recycler);
     // 執行addView
     // 測量view
}
//LinearLayoutManager.LayoutState
View next(Recycler recycler) {
	// mScrapList初始化爲空,且僅在layoutForPredictiveAnimations被賦值,執行完後又被設置爲null
    if (this.mScrapList != null) {
        return this.nextViewFromScrapList();
    } else {
    	// * 進入該分支
        View view = recycler.getViewForPosition(this.mCurrentPosition);
        this.mCurrentPosition += this.mItemDirection;
        return view;
    }
}
//RecyclerView.Recycler
@NonNull
public View getViewForPosition(int position) {
    return this.getViewForPosition(position, false);
}

View getViewForPosition(int position, boolean dryRun) {
    return this.tryGetViewHolderForPositionByDeadline(position, dryRun, 9223372036854775807L).itemView;
}
tryGetViewHolderForPositionByDeadline

該方法爲獲取緩存viewHolder核心方法

//RecyclerView.Recycler
@Nullable
RecyclerView.ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) {
    if (position >= 0 && position < RecyclerView.this.mState.getItemCount()) {
        boolean fromScrapOrHiddenOrCache = false;
        RecyclerView.ViewHolder holder = null;
        //[0] 從changed Scrap獲取holder
        if (RecyclerView.this.mState.isPreLayout()) {
        	//preLayout默認是false,只有有動畫的時候才爲true?
            holder = this.getChangedScrapViewForPosition(position);
            fromScrapOrHiddenOrCache = holder != null;
        }
		//[1] 從scrap中獲取holder
        if (holder == null) {
            holder = this.getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
            if (holder != null) {
            	// 檢查該holder是否與holder對應postion
                if (!this.validateViewHolderForOffsetPosition(holder)) {
                	// 如果信息不符
                    if (!dryRun) {
                        holder.addFlags(4);
                       	// 從scrap移除
                        if (holder.isScrap()) {
                            RecyclerView.this.removeDetachedView(holder.itemView, false);
                            holder.unScrap();
                        } else if (holder.wasReturnedFromScrap()) {
                            holder.clearReturnedFromScrapFlag();
                        }
						// 將該holder放入mCachedViews或者mRecyclerPool中
                        this.recycleViewHolderInternal(holder);
                    }
					//設置爲空
                    holder = null;
                } else {
                    fromScrapOrHiddenOrCache = true;
                }
            }
        }

        int offsetPosition;
        int type;
        if (holder == null) {
            offsetPosition = RecyclerView.this.mAdapterHelper.findPositionOffset(position);
            if (offsetPosition < 0 || offsetPosition >= RecyclerView.this.mAdapter.getItemCount()) {
                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item position " + position + "(offset:" + offsetPosition + ")." + "state:" + RecyclerView.this.mState.getItemCount() + RecyclerView.this.exceptionLabel());
            }

            type = RecyclerView.this.mAdapter.getItemViewType(offsetPosition);
            if (RecyclerView.this.mAdapter.hasStableIds()) {
                holder = this.getScrapOrCachedViewForId(RecyclerView.this.mAdapter.getItemId(offsetPosition), type, dryRun);
                if (holder != null) {
                    holder.mPosition = offsetPosition;
                    fromScrapOrHiddenOrCache = true;
                }
            }
			// 自定義緩存
            if (holder == null && this.mViewCacheExtension != null) {
                View view = this.mViewCacheExtension.getViewForPositionAndType(this, position, type);
                if (view != null) {
                    holder = RecyclerView.this.getChildViewHolder(view);
                    if (holder == null) {
                        throw new IllegalArgumentException("getViewForPositionAndType returned a view which does not have a ViewHolder" + RecyclerView.this.exceptionLabel());
                    }

                    if (holder.shouldIgnore()) {
                        throw new IllegalArgumentException("getViewForPositionAndType returned a view that is ignored. You must call stopIgnoring before returning this view." + RecyclerView.this.exceptionLabel());
                    }
                }
            }
			// 從緩存池中獲取
            if (holder == null) {
                holder = this.getRecycledViewPool().getRecycledView(type);
                if (holder != null) {
                    holder.resetInternal();
                    if (RecyclerView.FORCE_INVALIDATE_DISPLAY_LIST) {
                        this.invalidateDisplayListInt(holder);
                    }
                }
            }
			// 創建
            if (holder == null) {
                long start = RecyclerView.this.getNanoTime();
                if (deadlineNs != 9223372036854775807L && !this.mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
                    return null;
                }

                holder = RecyclerView.this.mAdapter.createViewHolder(RecyclerView.this, type);
                if (RecyclerView.ALLOW_THREAD_GAP_WORK) {
                    RecyclerView innerView = RecyclerView.findNestedRecyclerView(holder.itemView);
                    if (innerView != null) {
                        holder.mNestedRecyclerView = new WeakReference(innerView);
                    }
                }

                long end = RecyclerView.this.getNanoTime();
                this.mRecyclerPool.factorInCreateTime(type, end - start);
            }
        }
        
        if (RecyclerView.this.mState.isPreLayout() && holder.isBound()) {
            holder.mPreLayoutPosition = position;
        } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) {
        	// 如果ViewHolder是從recyclerPool中拿到的,由於其ViewHolder信息初始化則需要進行bindViewHolder
            type = RecyclerView.this.mAdapterHelper.findPositionOffset(position);
            bound = this.tryBindViewHolderByDeadline(holder, type, position, deadlineNs);
        }
      // .......  
       return holder;
    } else {
        throw new IndexOutOfBoundsException("Invalid item position " + position + "(" + position + "). Item count:" + RecyclerView.this.mState.getItemCount() + RecyclerView.this.exceptionLabel());
    }
}
if (RecyclerView.this.mState.isPreLayout()) {
	//preLayout默認是false,只有有動畫的時候才爲true?
    holder = this.getChangedScrapViewForPosition(position);
    fromScrapOrHiddenOrCache = holder != null;
}
//RecyclerView.State
 public boolean isPreLayout() {
     return this.mInPreLayout;
 }
=> this.mState.mInPreLayout = this.mState.mRunPredictiveAnimations;
=> this.mState.mRunPredictiveAnimations = this.mState.mRunSimpleAnimations && animationTypeSupported && !this.mDataSetHasChangedAfterLayout && this.predictiveItemAnimationsEnabled();
// 唉,索引到這裏我已經暈了,有的博主說當動畫執行的時候爲true
1、依據position從mAttachedScrap/HiddenViews/mCachedViews中獲取holder
if (holder == null) {
    holder = this.getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);
    if (holder != null) {
    	// 檢驗從scrap貨cache獲取的holder是否與position對應
        if (!this.validateViewHolderForOffsetPosition(holder)) {
            if (!dryRun) {
                holder.addFlags(4);
                // 如果信息不符合 scrap中移除該scrap
                if (holder.isScrap()) {
                    RecyclerView.this.removeDetachedView(holder.itemView, false);
                    holder.unScrap();
                } else if (holder.wasReturnedFromScrap()) {
                    holder.clearReturnedFromScrapFlag();
                }
                // 將該holder放入mCachedViews或mRecyclerPool
                this.recycleViewHolderInternal(holder);
            }
            holder = null;
        } else {
            fromScrapOrHiddenOrCache = true;
        }
    }
}
RecyclerView.ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun) {
    int scrapCount = this.mAttachedScrap.size();

    int cacheSize;
    RecyclerView.ViewHolder vh;
    // **[1] for循環遍歷mAttachedScrap獲取postion相等的viewHolder
    for(cacheSize = 0; cacheSize < scrapCount; ++cacheSize) {
        vh = (RecyclerView.ViewHolder)this.mAttachedScrap.get(cacheSize);
        if (!vh.wasReturnedFromScrap() && vh.getLayoutPosition() == position && !vh.isInvalid() && (RecyclerView.this.mState.mInPreLayout || !vh.isRemoved())) {
            vh.addFlags(32);
            return vh;
        }
    }
	// dryRun爲false
    if (!dryRun) {
    	// **[2]從隱藏但沒有移除的view中尋找
    	// todo 在哪裏進行添加的?
        View view = RecyclerView.this.mChildHelper.findHiddenNonRemovedView(position);
        if (view != null) {
            vh = RecyclerView.getChildViewHolderInt(view);
            RecyclerView.this.mChildHelper.unhide(view);
            int layoutIndex = RecyclerView.this.mChildHelper.indexOfChild(view);
            if (layoutIndex == -1) {
                throw new IllegalStateException("layout index should not be -1 after unhiding a view:" + vh + RecyclerView.this.exceptionLabel());
            }
RecyclerView.this.mChildHelper.detachViewFromParent(layoutIndex);
			// scrap緩存holder
            this.scrapView(view);
            vh.addFlags(8224);
            return vh;
        }
    }

    cacheSize = this.mCachedViews.size();
	// ** [3] 從CacheViews中去獲取
    for(int i = 0; i < cacheSize; ++i) {
        RecyclerView.ViewHolder holder = (RecyclerView.ViewHolder)this.mCachedViews.get(i);
        if (!holder.isInvalid() && holder.getLayoutPosition() == position) {
            if (!dryRun) {
                this.mCachedViews.remove(i);
            }

            return holder;
        }
    }
    return null;
}
type = RecyclerView.this.mAdapter.getItemViewType(offsetPosition);
 if (RecyclerView.this.mAdapter.hasStableIds()) {
     holder = this.getScrapOrCachedViewForId(RecyclerView.this.mAdapter.getItemId(offsetPosition), type, dryRun);
     if (holder != null) {
         holder.mPosition = offsetPosition;
         fromScrapOrHiddenOrCache = true;
     }
 }

mHasStableIds在Adapter中默認是false,而修改其值的api如下所示,
當我們在對Adapter設置setHasStableIds(true)時,注意得重寫getItemId

public void setHasStableIds(boolean hasStableIds) {
    if (this.hasObservers()) {
        throw new IllegalStateException("Cannot change whether this adapter has stable IDs while the adapter has registered observers.");
    } else {
        this.mHasStableIds = hasStableIds;
    }
}
2、依據ID type從mAttachedScrap和CacheView中獲取holder
RecyclerView.ViewHolder getScrapOrCachedViewForId(long id, int type, boolean dryRun) {
    int count = this.mAttachedScrap.size();
    int i;
    // for循環遍歷mAttachedScrap,找出id相等的holder
    for(i = count - 1; i >= 0; --i) {
        RecyclerView.ViewHolder holder = (RecyclerView.ViewHolder)this.mAttachedScrap.get(i);
        if (holder.getItemId() == id && !holder.wasReturnedFromScrap()) {
            if (type == holder.getItemViewType()) {
                holder.addFlags(32);
                if (holder.isRemoved() && !RecyclerView.this.mState.isPreLayout()) {
                    holder.setFlags(2, 14);
                }

                return holder;
            }
            if (!dryRun) {
                this.mAttachedScrap.remove(i);
                RecyclerView.this.removeDetachedView(holder.itemView, false);
                this.quickRecycleScrapView(holder.itemView);
            }
        }
    }
    // 以ID爲判斷標準從CachedView中尋找holder
    i = this.mCachedViews.size();
    for(int ix = i - 1; ix >= 0; --ix) {
        RecyclerView.ViewHolder holderx = (RecyclerView.ViewHolder)this.mCachedViews.get(ix);
        if (holderx.getItemId() == id) {
            if (type == holderx.getItemViewType()) {
                if (!dryRun) {
                    this.mCachedViews.remove(ix);
                }

                return holderx;
            }
            if (!dryRun) {
                this.recycleCachedViewAt(ix);
                return null;
            }
        }
    }
    return null;
}
3、自定義緩存獲取holder
4、依據type從mRecyclerPool獲取holder
if (holder == null) {
    holder = this.getRecycledViewPool().getRecycledView(type);
    if (holder != null) {
        holder.resetInternal();
        if (RecyclerView.FORCE_INVALIDATE_DISPLAY_LIST) {
            this.invalidateDisplayListInt(holder);
        }
    }
}
5、緩存都沒有重新創建
if (holder == null) {
    long start = RecyclerView.this.getNanoTime();
    if (deadlineNs != 9223372036854775807L && !this.mRecyclerPool.willCreateInTime(type, start, deadlineNs)) {
        return null;
    }
    holder = RecyclerView.this.mAdapter.createViewHolder(RecyclerView.this, type);
    if (RecyclerView.ALLOW_THREAD_GAP_WORK) {
        RecyclerView innerView = RecyclerView.findNestedRecyclerView(holder.itemView);
        if (innerView != null) {
            holder.mNestedRecyclerView = new WeakReference(innerView);
        }
    }

    long end = RecyclerView.this.getNanoTime();
    this.mRecyclerPool.factorInCreateTime(type, end - start);
}

問題探究

Q1: mAttachedScrap何時緩存列表數據?何時銷燬緩存?

首先探索何時賦值,我們逆着推Recycler中mAttachedScrap何時調用的
mAttachedScrap 緩存holder

// RecyclerView.java
public final class Recycler {
	void scrapView(View view) {
	   ....
	   this.mAttachedScrap.add(holder);
	}
}
// RecyclerView.java
public abstract static class LayoutManager {
	// .....
	private void scrapOrRecycleView(RecyclerView.Recycler recycler, int index, View view) {
	    RecyclerView.ViewHolder viewHolder = RecyclerView.getChildViewHolderInt(view);
	    if (!viewHolder.shouldIgnore()) {
	        if (viewHolder.isInvalid() && !viewHolder.isRemoved() && !this.mRecyclerView.mAdapter.hasStableIds()) {
	        	// layoutManager刪除該項
	            this.removeViewAt(index);
	            // 將holder放入cacheView或recyclerViewPool中
	            recycler.recycleViewHolderInternal(viewHolder);
	        } else {
	            this.detachViewAt(index);
	            // 將view加入mAttchScraps中
	            recycler.scrapView(view);
	            this.mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
	        }
	    }
	}
	
	=>  // scrapOrRecycleView 在該方法中調用
   public void detachAndScrapAttachedViews(@NonNull RecyclerView.Recycler recycler) {
   	   // 獲取layout可見的佈局  	
       int childCount = this.getChildCount();
       for(int i = childCount - 1; i >= 0; --i) {
           View v = this.getChildAt(i);
           // 調用
           this.scrapOrRecycleView(recycler, i, v);
       }

   }
}
//LinearlayoutManager.java
public class LinearLayoutManager extends LayoutManager{
	public void onLayoutChildren(Recycler recycler, State state) {
		  // 在fill之前
		  // 在填充表項之前回收所有表項
		  this.detachAndScrapAttachedViews(recycler);
		  //...
		  this.fill(recycler, this.mLayoutState, state, false);
	}
	// onLayoutChildren 就是在 dispatchLayoutStep2()進行的調用
}

在將表項一個個填充到列表之前會先將其先回收到mAttachedScrap中,回收數據的來源是LayoutManager的孩子,而LayoutManager的孩子都是屏幕上可見的數據項。
mAttachedScrap用於屏幕中可見數據項的回收和複用

mAttachedScrap 清除緩存

//RecyclerView.java
public final class Recycler{
	 void clearScrap() {
	     this.mAttachedScrap.clear();
	     if (this.mChangedScrap != null) {
	         this.mChangedScrap.clear();
	     }
	 }
}
// RecyclerView.java
public abstract static class LayoutManager {
        /**
         * Recycles the scrapped views.
         * 回收所有scrapped view
         */
        void removeAndRecycleScrapInt(Recycler recycler) {
            final int scrapCount = recycler.getScrapCount();
            // Loop backward, recycler might be changed by removeDetachedView()
            //遍歷搜有scrap view並重置ViewHolder狀態
            for (int i = scrapCount - 1; i >= 0; i--) {
                final View scrap = recycler.getScrapViewAt(i);
                final ViewHolder vh = getChildViewHolderInt(scrap);
                if (vh.shouldIgnore()) {
                    continue;
                }
                vh.setIsRecyclable(false);
                if (vh.isTmpDetached()) {
                    mRecyclerView.removeDetachedView(scrap, false);
                }
                if (mRecyclerView.mItemAnimator != null) {
                    mRecyclerView.mItemAnimator.endAnimation(vh);
                }
                vh.setIsRecyclable(true);
                recycler.quickRecycleScrapView(scrap);
            }
            //清空scrap view集合
            recycler.clearScrap();
            if (scrapCount > 0) {
                mRecyclerView.invalidate();
            }
        }
}
private void dispatchLayoutStep3() {
	//....
	this.mLayout.removeAndRecycleScrapInt(this.mRecycler);
	//....
}

mAttachedScrap 就是在開始佈局前設置緩存,在佈局結束後清除緩存。
主要用於屏幕間可見數據的回收和複用。

Recycler回收機制

RecyclerView滑動時,當有可用空間時,首先會從緩存或重新創建view,然後進行添加,然後會依據頂部或底部的view是否會滑出屏幕,如果是的話,會進行回收,添加入CacheViews中。
從緩存中獲取view時,如果CachedViews中存在,則會將holder從list中刪除並返回;
如果cacheViews中找不到,則會從RecyclerPool中依據type進行尋找,找到之後還需執行bindViewHolder刷新數據。

   // RecyclerView.java	
   void scrollStep(int dx, int dy, @Nullable int[] consumed) {
   		//....
        if (dy != 0) {
            consumedY = this.mLayout.scrollVerticallyBy(dy, this.mRecycler, this.mState);
        }
}
 //LinearLayoutManager
 public int scrollVerticallyBy(int dy, Recycler recycler, State state) {
     return this.mOrientation == 0 ? 0 : this.scrollBy(dy, recycler, state);
 }

int scrollBy(int dy, Recycler recycler, State state) {
    int consumed = this.mLayoutState.mScrollingOffset + this.fill(recycler, this.mLayoutState, state, false);
    //...
}
	//LinearLayoutManager
int fill(Recycler recycler, LinearLayoutManager.LayoutState layoutState, State state, boolean stopOnFocusable) {
    while((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
        layoutChunkResult.resetInternal();
        this.layoutChunk(recycler, state, layoutState, layoutChunkResult);
        // ....
        if (layoutState.mScrollingOffset != -2147483648) {
            this.recycleByLayoutState(recycler, layoutState);
        }
    }
    return start - layoutState.mAvailable;
}
//RecyclerView.java
private void recycleByLayoutState(Recycler recycler, LinearLayoutManager.LayoutState layoutState) {
    if (layoutState.mRecycle && !layoutState.mInfinite) {
        if (layoutState.mLayoutDirection == -1) {
            this.recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
        } else {
            this.recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
        }

    }
}

private void recycleViewsFromStart(Recycler recycler, int dt) {
    if (dt >= 0) {
        int limit = dt;
        int childCount = this.getChildCount();
        int i;
        View child;
        if (this.mShouldReverseLayout) {
            for(i = childCount - 1; i >= 0; --i) {
                child = this.getChildAt(i);
                if (this.mOrientationHelper.getDecoratedEnd(child) > limit || this.mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
                    this.recycleChildren(recycler, childCount - 1, i);
                    return;
                }
            }
        } else {
            for(i = 0; i < childCount; ++i) {
                child = this.getChildAt(i);
                if (this.mOrientationHelper.getDecoratedEnd(child) > limit || this.mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
                    this.recycleChildren(recycler, 0, i);
                    return;
                }
            }
        }

    }
}

private void recycleChildren(Recycler recycler, int startIndex, int endIndex) {
    if (startIndex != endIndex) {
        int i;
        if (endIndex > startIndex) {
            for(i = endIndex - 1; i >= startIndex; --i) {
                this.removeAndRecycleViewAt(i, recycler);
            }
        } else {
            for(i = startIndex; i > endIndex; --i) {
                this.removeAndRecycleViewAt(i, recycler);
            }
        }

    }
}

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

回收滑出屏幕對應的ViewHolder

# RecyclerView.Recycler
public void recycleView(@NonNull View view) {
    RecyclerView.ViewHolder holder = RecyclerView.getChildViewHolderInt(view);
    if (holder.isTmpDetached()) {
        RecyclerView.this.removeDetachedView(view, false);
    }

    if (holder.isScrap()) {
        holder.unScrap();
    } else if (holder.wasReturnedFromScrap()) {
        holder.clearReturnedFromScrapFlag();
    }

    this.recycleViewHolderInternal(holder);
}

void recycleViewHolderInternal(RecyclerView.ViewHolder holder) {
	// ....
  if (forceRecycle || holder.isRecyclable()) {
     if (this.mViewCacheMax > 0 && !holder.hasAnyOfTheFlags(526)) {
         int cachedViewSize = this.mCachedViews.size();
         // 當緩存的數量大於最大緩存數量時,mCachedViews會移除最老的一個view
         if (cachedViewSize >= this.mViewCacheMax && cachedViewSize > 0) {
         	// 移除cacheView中數據
             this.recycleCachedViewAt(0);
             --cachedViewSize;
         }
         int targetCacheIndex = cachedViewSize;
         if (RecyclerView.ALLOW_THREAD_GAP_WORK && cachedViewSize > 0 && !RecyclerView.this.mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) {
             int cacheIndex;
             for(cacheIndex = cachedViewSize - 1; cacheIndex >= 0; --cacheIndex) {
                 int cachedPos = ((RecyclerView.ViewHolder)this.mCachedViews.get(cacheIndex)).mPosition;
                 if (!RecyclerView.this.mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) {
                     break;
                 }
             }
             targetCacheIndex = cacheIndex + 1;
         }
		 // ** 將holder加入mCacheViews中	
         this.mCachedViews.add(targetCacheIndex, holder);
         cached = true;
     }

     if (!cached) {
     	 // 如果沒有加入mCacheViews則將holder加入RecyclerPool中
         this.addViewHolderToRecycledViewPool(holder, true);
         recycled = true;
     }
 }
}
//RecyclerView.java
// 將mCacheViews中ViewHolder放入recyclerPool中
public final class Recycler{
	void recycleCachedViewAt(int cachedViewIndex) {
	    RecyclerView.ViewHolder viewHolder = (RecyclerView.ViewHolder)this.mCachedViews.get(cachedViewIndex);
	    this.addViewHolderToRecycledViewPool(viewHolder, true);
	    this.mCachedViews.remove(cachedViewIndex);
	}
}

從mCachedViews移除掉的ViewHolder會加入到回收池中。 mCachedViews有點像“回收池預備隊列”,即總是先回收到mCachedViews,當它放不下的時候,按照先進先出原則將最先進入的ViewHolder存入回收池

// RecyclerView.java
public staitic final Recycler{
  void addViewHolderToRecycledViewPool(@NonNull RecyclerView.ViewHolder holder, boolean dispatchRecycled) {
      if (dispatchRecycled) {
          this.dispatchViewRecycled(holder);
      }
      holder.mOwnerRecyclerView = null;
      this.getRecycledViewPool().putRecycledView(holder);
  }
  
 // 進行一些回調 
 void dispatchViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
     if (RecyclerView.this.mRecyclerListener != null) {
         RecyclerView.this.mRecyclerListener.onViewRecycled(holder);
     }
     if (RecyclerView.this.mAdapter != null) {
         RecyclerView.this.mAdapter.onViewRecycled(holder);
     }
     if (RecyclerView.this.mState != null) {
         RecyclerView.this.mViewInfoStore.removeViewHolder(holder);
     }
 }
}
// RecyclerView.java
public static class RecycledViewPool {
  public void putRecycledView(RecyclerView.ViewHolder scrap) {
      int viewType = scrap.getItemViewType();
      ArrayList<RecyclerView.ViewHolder> scrapHeap = this.getScrapDataForType(viewType).mScrapHeap;
      if (((RecyclerView.RecycledViewPool.ScrapData)this.mScrap.get(viewType)).mMaxScrap > scrapHeap.size()) {
      	  // 在緩存前重置ViewHolder內部信息,包括position,itemID之類
      	  // 這樣下次從recyclerPool中拿出來複用時就可以當一個新的ViewHolder使用
          scrap.resetInternal();
          scrapHeap.add(scrap);
      }
  }
  
void resetInternal() {
     this.mFlags = 0;
     this.mPosition = -1;
     this.mOldPosition = -1;
     this.mItemId = -1L;
     this.mPreLayoutPosition = -1;
     this.mIsRecyclableCount = 0;
     this.mShadowedHolder = null;
     this.mShadowingHolder = null;
     this.clearPayload();
     this.mWasImportantForAccessibilityBeforeHidden = 0;
     this.mPendingAccessibilityState = -1;
     RecyclerView.clearNestedRecyclerViewIfNotNested(this);
 }
}
Q: mCachedViews中 mViewCacheMax默認值時是多少?可以修改?如何修改呢?
// RecyclerView.java
public final class Recycler {
  public Recycler() {
     this.mUnmodifiableAttachedScrap = Collections.unmodifiableList(this.mAttachedScrap);
     this.mRequestedCacheMax = 2;
     
     this.mViewCacheMax = 2;
  }
  
	public void setViewCacheSize(int viewCount) {
	    this.mRequestedCacheMax = viewCount;
	    this.updateViewCacheSize();
	}
	// 更新緩存的最大數量
	void updateViewCacheSize() {
	    int extraCache = RecyclerView.this.mLayout != null ? RecyclerView.this.mLayout.mPrefetchMaxCountObserved : 0// 設置最大緩存數量
	    this.mViewCacheMax = this.mRequestedCacheMax + extraCache;
	    for(int i = this.mCachedViews.size() - 1; i >= 0 && this.mCachedViews.size() > this.mViewCacheMax; --i) {
	        this.recycleCachedViewAt(i);
	    }
	
	}
}
 // RecyclerView.java
 public void setItemViewCacheSize(int size) {
     this.mRecycler.setViewCacheSize(size);
 }

mCacheViews默認緩存的數量是2,我們可通過調用RecyclerView的setItemViewCacheSize來設置Recycler中mCacheViews的最大數量

計算平移的距離,遍歷子view進行平移滑動平移

public void offsetChildrenVertical(@Px int dy) {
    int childCount = this.mChildHelper.getChildCount();

    for(int i = 0; i < childCount; ++i) {
        this.mChildHelper.getChildAt(i).offsetTopAndBottom(dy);
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章