publicvoidsetAdapter(Adapter adapter){ // bail out if layout is frozen setLayoutFrozen(false); setAdapterInternal(adapter, false, true); requestLayout(); }
voiddefaultOnMeasure(int widthSpec, int heightSpec){ // calling LayoutManager here is not pretty but that API is already public and it is better // than creating another method since this is internal. finalint width = LayoutManager.chooseSize(widthSpec, getPaddingLeft() + getPaddingRight(), getMinimumWidth()); finalint height = LayoutManager.chooseSize(heightSpec, getPaddingTop() + getPaddingBottom(), getMinimumHeight());
setMeasuredDimension(width, height); }
// LayoutManager.java publicstaticintchooseSize(int spec, int desired, int min){ finalint mode = View.MeasureSpec.getMode(spec); finalint size = View.MeasureSpec.getSize(spec); switch (mode) { case View.MeasureSpec.EXACTLY: return size; case View.MeasureSpec.AT_MOST: return Math.min(size, Math.max(desired, min)); case View.MeasureSpec.UNSPECIFIED: default: return Math.max(desired, min); } }
@Override protectedvoidonLayout(boolean changed, int l, int t, int r, int b){ Trace.beginSection(TRACE_ON_LAYOUT_TAG); dispatchLayout(); Trace.endSection(); mFirstLayoutComplete = true; }
voiddispatchLayout(){ ... mState.mIsMeasuring = false; //第一次开始布局 if (mState.mLayoutStep == State.STEP_START) { //存储ziView状态并确定是否要执行动画 dispatchLayoutStep1(); mLayout.setExactMeasureSpecsFrom(this); dispatchLayoutStep2(); //发生了数据变化(notifyDataSetChanges)或者布局变化 } elseif (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth() || mLayout.getHeight() != getHeight()) { // First 2 steps are done in onMeasure but looks like we have to run again due to // changed size. mLayout.setExactMeasureSpecsFrom(this); //Item布局过程 dispatchLayoutStep2(); } else { // always make sure we sync them (to ensure mode is exact) mLayout.setExactMeasureSpecsFrom(this); } //执行Item 动画 dispatchLayoutStep3(); }
if (mLayoutState.mAvailable > 0) { // end could not consume all. add more items towards start extraForStart = mLayoutState.mAvailable; updateLayoutStateToFillStart(firstElement, startOffset); mLayoutState.mExtra = extraForStart; fill(recycler, mLayoutState, state, false); startOffset = mLayoutState.mOffset; } }else{ ...
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { right = layoutState.mOffset; left = layoutState.mOffset - result.mConsumed; } else { left = layoutState.mOffset; right = layoutState.mOffset + result.mConsumed; } 2} // item布局 2layoutDecoratedWithMargins(view, left, top, right, bottom); ... }
//测量子View的布局 publicvoidmeasureChildWithMargins(View child, int widthUsed, int heightUsed){ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child); widthUsed += insets.left + insets.right; heightUsed += insets.top + insets.bottom;
publicvoidlayoutDecoratedWithMargins(View child, int left, int top, int right, int bottom){ final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final Rect insets = lp.mDecorInsets; //子view布局 child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin, right - insets.right - lp.rightMargin, bottom - insets.bottom - lp.bottomMargin); }
class CustomItemAnimator : RecyclerView.ItemAnimator(){
override fun isRunning(): Boolean { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. }
override fun animatePersistence( viewHolder: RecyclerView.ViewHolder, preLayoutInfo: ItemHolderInfo, postLayoutInfo: ItemHolderInfo ): Boolean { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. }
override fun runPendingAnimations() { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. }
override fun endAnimation(item: RecyclerView.ViewHolder) { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. }
override fun animateDisappearance( viewHolder: RecyclerView.ViewHolder, preLayoutInfo: ItemHolderInfo, postLayoutInfo: ItemHolderInfo? ): Boolean { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. }
override fun animateChange( oldHolder: RecyclerView.ViewHolder, newHolder: RecyclerView.ViewHolder, preLayoutInfo: ItemHolderInfo, postLayoutInfo: ItemHolderInfo ): Boolean { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. }
override fun animateAppearance( viewHolder: RecyclerView.ViewHolder, preLayoutInfo: ItemHolderInfo?, postLayoutInfo: ItemHolderInfo ): Boolean { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. }
override fun endAnimations() { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. }
public View getViewForPosition(int position){ return getViewForPosition(position, false); }
View getViewForPosition(int position, boolean dryRun){ return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView; } //复用机制的主要方实现 ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs){ //判断位置是否正常 if (position < 0 || position >= mState.getItemCount()) { thrownew IndexOutOfBoundsException("Invalid item position " + position + "(" + position + "). Item count:" + mState.getItemCount()); } boolean fromScrapOrHiddenOrCache = false; ViewHolder holder = null; // 是否设置动画 if (mState.isPreLayout()) { holder = getChangedScrapViewForPosition(position); fromScrapOrHiddenOrCache = holder != null; }
if (holder == null) { //从 mAttcherScrap 或者 mCachedViews 获取holder holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun);① if (holder != null) { //判定ViewHolder是否有效 if (!validateViewHolderForOffsetPosition(holder)) {② // recycle holder (and unscrap if relevant) since it can't be used if (!dryRun) { //设置回收标记 holder.addFlags(ViewHolder.FLAG_INVALID); if (holder.isScrap()) { removeDetachedView(holder.itemView, false); holder.unScrap(); } elseif (holder.wasReturnedFromScrap()) { holder.clearReturnedFromScrapFlag(); } recycleViewHolderInternal(holder); } holder = null; } else { fromScrapOrHiddenOrCache = true; } } }
if (holder == null) { finalint offsetPosition = mAdapterHelper.findPositionOffset(position); if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) { thrownew IndexOutOfBoundsException("Inconsistency detected. Invalid item " + "position " + position + "(offset:" + offsetPosition + ")." + "state:" + mState.getItemCount()); } //获取item设置的type finalint type = mAdapter.getItemViewType(offsetPosition); // stable id 就是标识一个viewholder的唯一性 if (mAdapter.hasStableIds()) { holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),③ type, dryRun); if (holder != null) { // update position holder.mPosition = offsetPosition; fromScrapOrHiddenOrCache = true; } } //从用户自己设置的 mViewCacheExtension 去寻找对应ViewHolder· 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 (view != null) { //包装成一个ViewHolder holder = getChildViewHolder(view); if (holder == null) { thrownew IllegalArgumentException("getViewForPositionAndType returned" + " a view which does not have a ViewHolder"); } elseif (holder.shouldIgnore()) { thrownew IllegalArgumentException("getViewForPositionAndType returned" + " a view that is ignored. You must call stopIgnoring before" + " returning this view."); } } } //从 RecyclerViewPool去寻找对应的ViewHolder if (holder == null) { // fallback to pool if (DEBUG) { Log.d(TAG, "tryGetViewHolderForPositionByDeadline(" + position + ") fetching from shared pool"); } holder = getRecycledViewPool().getRecycledView(type);④ if (holder != null) { holder.resetInternal();⑤ if (FORCE_INVALIDATE_DISPLAY_LIST) { invalidateDisplayListInt(holder); } } } //从以上三级缓存中都没有找到对应的ViewHolder就只能自己创建了 if (holder == null) { long start = getNanoTime(); if (deadlineNs != FOREVER_NS && !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) { // abort - we have a deadline we can't meet returnnull; } //创建对应的ViewHolder holder = mAdapter.createViewHolder(RecyclerView.this, type); if (ALLOW_THREAD_GAP_WORK) { // only bother finding nested RV if prefetching RecyclerView innerView = findNestedRecyclerView(holder.itemView); if (innerView != null) { holder.mNestedRecyclerView = new WeakReference<>(innerView); } }
long end = getNanoTime(); mRecyclerPool.factorInCreateTime(type, end - start); if (DEBUG) { Log.d(TAG, "tryGetViewHolderForPositionByDeadline created new ViewHolder"); } } }
// This is very ugly but the only place we can grab this information // before the View is rebound and returned to the LayoutManager for post layout ops. // We don't need this in pre-layout since the VH is not updated by the LM. if (fromScrapOrHiddenOrCache && !mState.isPreLayout() && holder .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) { holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST); if (mState.mRunSimpleAnimations) { int changeFlags = ItemAnimator .buildAdapterChangeFlagsForAnimations(holder); changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT; final ItemHolderInfo info = mItemAnimator.recordPreLayoutInformation(mState, holder, changeFlags, holder.getUnmodifiedPayloads()); recordAnimationInfoIfBouncedHiddenView(holder, info); } }
boolean bound = false; if (mState.isPreLayout() && holder.isBound()) { // do not update unless we absolutely have to. holder.mPreLayoutPosition = position; } elseif (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) { if (DEBUG && holder.isRemoved()) { thrownew IllegalStateException("Removed holder should be bound and it should" + " come here only in pre-layout. Holder: " + holder); } finalint offsetPosition = mAdapterHelper.findPositionOffset(position); bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs); } //设置对应ViewHolder的 layoutparams final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); final LayoutParams rvLayoutParams; if (lp == null) { rvLayoutParams = (LayoutParams) generateDefaultLayoutParams(); holder.itemView.setLayoutParams(rvLayoutParams); } elseif (!checkLayoutParams(lp)) { rvLayoutParams = (LayoutParams) generateLayoutParams(lp); holder.itemView.setLayoutParams(rvLayoutParams); } else { rvLayoutParams = (LayoutParams) lp; } rvLayoutParams.mViewHolder = holder; rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound; return holder; }
ViewHolder getScrapOrHiddenOrCachedHolderForPosition(int position, boolean dryRun){ //先从 mAttachedScrap 获取对应的ViewHolder for (int i = 0; i < scrapCount; i++) { final ViewHolder holder = mAttachedScrap.get(i); if (!holder.wasReturnedFromScrap() && holder.getLayoutPosition() == position && !holder.isInvalid() && (mState.mInPreLayout || !holder.isRemoved())) { holder.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP); return holder; } } //从隐藏的View中去寻找ViewHolder if (!dryRun) { View view = mChildHelper.findHiddenNonRemovedView(position); if (view != null) { // This View is good to be used. We just need to unhide, detach and move to the // scrap list. final ViewHolder vh = getChildViewHolderInt(view); mChildHelper.unhide(view); int layoutIndex = mChildHelper.indexOfChild(view); if (layoutIndex == RecyclerView.NO_POSITION) { thrownew IllegalStateException("layout index should not be -1 after " + "unhiding a view:" + vh); } mChildHelper.detachViewFromParent(layoutIndex); scrapView(view); vh.addFlags(ViewHolder.FLAG_RETURNED_FROM_SCRAP | ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST); return vh; } } //从mCachedViews去获取对应的ViewHolder finalint cacheSize = mCachedViews.size(); for (int i = 0; i < cacheSize; i++) { final ViewHolder holder = mCachedViews.get(i); // invalid view holders may be in cache if adapter has stable ids as they can be // retrieved via getScrapOrCachedViewForId if (!holder.isInvalid() && holder.getLayoutPosition() == position) { if (!dryRun) { mCachedViews.remove(i); } if (DEBUG) { Log.d(TAG, "getScrapOrHiddenOrCachedHolderForPosition(" + position + ") found match in cache: " + holder); } return holder; } } }
booleanvalidateViewHolderForOffsetPosition(ViewHolder holder){ // if it is a removed holder, nothing to verify since we cannot ask adapter anymore // if it is not removed, verify the type and id. if (holder.isRemoved()) { if (DEBUG && !mState.isPreLayout()) { thrownew IllegalStateException("should not receive a removed view unless it" + " is pre layout"); } return mState.isPreLayout(); } if (holder.mPosition < 0 || holder.mPosition >= mAdapter.getItemCount()) { thrownew IndexOutOfBoundsException("Inconsistency detected. Invalid view holder " + "adapter position" + holder); } if (!mState.isPreLayout()) { // don't check type if it is pre-layout. finalint type = mAdapter.getItemViewType(holder.mPosition); if (type != holder.getItemViewType()) { returnfalse; } } if (mAdapter.hasStableIds()) { return holder.getItemId() == mAdapter.getItemId(holder.mPosition); } returntrue; }
int targetCacheIndex = cachedViewSize; if (ALLOW_THREAD_GAP_WORK && cachedViewSize > 0 && !mPrefetchRegistry.lastPrefetchIncludedPosition(holder.mPosition)) { // when adding the view, skip past most recently prefetched views int cacheIndex = cachedViewSize - 1; while (cacheIndex >= 0) { int cachedPos = mCachedViews.get(cacheIndex).mPosition; //缓存的时候不能覆盖最近经常使用的缓存 利用LFU算法 -- 最少使用策略 if (!mPrefetchRegistry.lastPrefetchIncludedPosition(cachedPos)) { break; } cacheIndex--; } targetCacheIndex = cacheIndex + 1; } //将最新的ViewHolder缓存数据插入到mCacheViews中复用 mCachedViews.add(targetCacheIndex, holder); cached = true; } //如果没有触发缓存的话 就放进RecyclerViewPool中 if (!cached) { addViewHolderToRecycledViewPool(holder, true); recycled = true; } } else { // NOTE: A view can fail to be recycled when it is scrolled off while an animation // runs. In this case, the item is eventually recycled by // ItemAnimatorRestoreListener#onAnimationFinished.
// TODO: consider cancelling an animation when an item is removed scrollBy, // to return it to the pool faster if (DEBUG) { Log.d(TAG, "trying to recycle a non-recycleable holder. Hopefully, it will " + "re-visit here. We are still removing it from animation lists"); } } // even if the holder is not removed, we still call this method so that it is removed // from view holder lists. mViewInfoStore.removeViewHolder(holder); if (!cached && !recycled && transientStatePreventsRecycling) { holder.mOwnerRecyclerView = null; } }