2023年7月15日发(作者:)
RecyclerView源码解析复⽤和回收复⽤的好处:避免为表项视图绑定数据,创建表项视图。⼦item的绘制交给LayoutManager去处理。fillLinearLayoutManager#fill作⽤:回收和复⽤。int fill(er recycler, LayoutState layoutState, state, boolean stopOnFocusable) { ... // 当前的⽅向上是否还有多余的空间填充item int remainingSpace = able + ; LayoutChunkResult layoutChunkResult = mLayoutChunkResult; // 当剩余空间> 0时,继续填充更多表项 while ((ite || remainingSpace > 0) && e(state)) { // 通过View循环,来对条⽬进⾏⼀条条复⽤,填充剩余空间 layoutChunk(recycler, state, layoutState, layoutChunkResult);
if (lingOffset != ING_OFFSET_NaN) { // 从剩余空间中扣除新表项占⽤像素值 lingOffset += med; if (able < 0) { // 在limit上追加新表项所占像素值 // 回收哪些项⽬是根据limit线⾛的,⼿指向上滑,底部填充元素,limit线会下移,在这根线上⾯的条⽬会被回收。 lingOffset += able; } // 回收 recycleByLayoutState(recycler, layoutState); } } return start - able;}回收LinearLarLayoutManager#recycleByLayoutStateprivate void recycleByLayoutState(er recycler, LayoutState layoutState) { if (tDirection == _START) { // 从列表头回收 recycleViewsFromEnd(recycler, lingOffset); } else { // 从列表尾回收 recycleViewsFromStart(recycler, lingOffset); }}LinearLarLayoutManager#recycleViewsFrsFroomStartprivate void recycleViewsFromStart(er recycler, int dt) { //从头开始遍历 LinearLayoutManager,以找出应该会回收的表项 final int childCount = getChildCount(); // 是否反转布局,就是布局上从上往下填充还是从下往上填充 if (mShouldReverseLayout) { for (int i = childCount - 1; i >= 0; i--) { View child = getChildAt(i); // 当某表项底部位于limit隐形线之后时,回收它以上的所有表项 // limit是列表中隐形的线 if (oratedEnd(child) > limit || nsformedEndWithDecoration(child) > limit) { //回收索引为末尾到i-1的表项 recycleChildren(recycler, childCount - 1, i); return; } } } else { //回收索引为0到i-1的表项 for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (oratedEnd(child) > limit || nsformedEndWithDecoration(child) > limit) { recycleChildren(recycler, 0, i); return; } } }}“从列表头回收表项”所对应的场景是:⼿指上滑,列表向上滚动,新的表项逐个插⼊到列表尾部,列表头部的表项逐个被回收。复⽤void layoutChunk(er recycler, state, LayoutState layoutState, LayoutChunkResult result) { // 1. 通过缓存池中获取下个条⽬ View view = (recycler);
// 2. 将列表中的⼀项添加进RecyclerView Params params = (Params) outParams(); // 3. 测量该视图 measureChildWithMargins(view, 0, 0); // 4. 获取填充视图需要消耗的像素值 med = oratedMeasurement(view); // 5. 布局表项 // 确定表项上下左右四个点相对于RecyclerView的位置 layoutDecoratedWithMargins(view, left, top, right, bottom);}LinearLarLayoutManager#nextView next(er recycler) { if (mScrapList != null) { return nextViewFromScrapList(); } final View view = wForPosition(mCurrentPosition); mCurrentPosition += mItemDirection; return view;}RecyclerView#tryGetViewHolderFolderForPositionByDeadline复⽤机制代码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 // 复⽤的对象是ViewHolder // 在布局之前 if (ayout()) { // 1. 通过id或者position从mChangedScrap缓存找到对应的缓存 holder = getChangedScrapViewForPosition(position); fromScrapOrHiddenOrCache = holder != null; } if (holder == null) { // 2. 通过position从mAttachedScrap或⼆级回收缓存中获取ViewHolder holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun); if (holder != null) { if (!validateViewHolderForOffsetPosition(holder)) { if (!dryRun) { gs(_INVALID); if (p()) { removeDetachedView(ew, false); p(); } else if (urnedFromScrap()) { eturnedFromScrapFlag(); } recycleViewHolderInternal(holder); } holder = null; } else { fromScrapOrHiddenOrCache = true; } } } if (holder == null) { final int offsetPosition = sitionOffset(position); final int type = mViewType(offsetPosition); // 3. 通过id从mAttachedScrap或⼆级回收缓存中获取ViewHolder if (bleIds()) { holder = getScrapOrCachedViewForId(mId(offsetPosition), type, dryRun); if (holder != null) { // update position ion = offsetPosition; fromScrapOrHiddenOrCache = true; } } if (holder == null && mViewCacheExtension != null) { // 4. 从⾃定义缓存中获取ViewHolder final View view = mViewCacheExtension .getViewForPositionAndType(this, position, type); if (view != null) { holder = getChildViewHolder(view); } } if (holder == null) { // fallback to pool // 5. 从缓存池中取ViewHolder holder = getRecycledViewPool().getRecycledView(type); if (holder != null) { nternal(); if (FORCE_INVALIDATE_DISPLAY_LIST) { invalidateDisplayListInt(holder); } } } if (holder == null) { long start = getNanoTime(); // 6.所有缓存都没命中,就需要创建ViewHolder holder = ViewHolder(, type); holder = ViewHolder(, type); if (ALLOW_THREAD_GAP_WORK) { // only bother finding nested RV if prefetching RecyclerView innerView = findNestedRecyclerView(ew); if (innerView != null) { dRecyclerView = new WeakReference<>(innerView); } } long end = getNanoTime(); InCreateTime(type, end - start); if (DEBUG) { Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder"); } } } if (fromScrapOrHiddenOrCache && !ayout() && holder .hasAnyOfTheFlags(_BOUNCED_FROM_HIDDEN_LIST)) { gs(0, _BOUNCED_FROM_HIDDEN_LIST); if (mpleAnimations) { int changeFlags = ItemAnimator .buildAdapterChangeFlagsForAnimations(holder); changeFlags |= _APPEARED_IN_PRE_LAYOUT; final ItemHolderInfo info = PreLayoutInformation(mState, holder, changeFlags, odifiedPayloads()); recordAnimationInfoIfBouncedHiddenView(holder, info); } } boolean bound = false; if (ayout() && d()) { youtPosition = position; } else if (!d() || pdate() || lid()) { final int offsetPosition = sitionOffset(position); //获得ViewHolder后,绑定视图数据 bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs); } ... return holder;}总结:RecyclerView在滚动发⽣之前,会根据预计滚动位移⼤⼩来决定需要向列表中填充多少新的表项。在填充表项的同时,也会回收表项,回收的依据是limit隐形线。limit隐形线是RecyclerView在滚动发⽣之前根据滚动位移计算出来的⼀条线,它是决定哪些表项该被回收的重要依据。它可以理解为:隐形线当前所在位置,在滚动完成后会和列表顶部重叠。limit隐形线的初始值=列表当前可见表项的底部到列表底部的距离,即列表在不填充新表项时,可以滑动的最⼤距离。每⼀个新填充表项消耗的像素值都会被追加到limit值之上,即limit隐形线会随着新表项的填充⽽不断地下移。触发回收逻辑时,会遍历当前所有表项,若某表项的底部位于limit隐形线下⽅,则该表项上⽅的所有表项都会被回收。四级缓存// detach调⽤// 复⽤的时候不需要调⽤bindViewHolder重新绑定数据,状态和数据不会被重置的// 保存原封不动的ViewHolder// ⽣命周期两次布局// 位置⼀致才能复⽤final ArrayList
mCachedViews、ViewCacheExtension mViewCacheExtension、RecycledViewPool mRecyclerPool。如果四层缓存都未命中,则重新创建并绑定ViewHolder对象。缓存性能:都不需要重新创建ViewHolder,只有RecycledViewPool,mChangedScrap需要重新绑定数据。缓存容量:mAttachedScrap:没有⼤⼩限制,但最多包含屏幕可见表项。mCachedViews:默认⼤⼩限制为2,放不下时,按照先进先出原则将最先进⼊的ViewHolder存⼊回收池以腾出空间。mRecyclerPool:对ViewHolder按viewType分类存储(通过SparseArray),同类ViewHolder存储在默认⼤⼩为5的ArrayList中。缓存⽤途:mAttachedScrap:⽤于布局过程中屏幕可见表项的回收和复⽤。mCachedViews:⽤于移出屏幕表项的回收和复⽤,且只能⽤于指定位置的表项,有点像“回收池预备队列”,即总是先回收到mCachedViews,当它放不下的时候,按照先进先出原则将最先进⼊的ViewHolder存⼊回收池。mRecyclerPool:⽤于移出屏幕表项的回收和复⽤,且只能⽤于指定viewType的表项缓存结构:mAttachedScrap:ArrayList
发布者:admin,转转请注明出处:http://www.yc00.com/web/1689431291a247187.html
评论列表(0条)