@Override protectedvoidonMeasure(int widthSpec, int heightSpec){ // 如果LayoutManager为空,则走默认的measure逻辑 if (mLayout == null) { defaultOnMeasure(widthSpec, heightSpec); return; } if (mLayout.mAutoMeasure) { // 如果LayoutManager设置了setAutoMeasureEnabled为true // 第一部分: // 1) 调用LayoutManager#onMeasure,在其内部调用了RecyclerView#defaultOnMeasure,然后调用LayoutManager.chooseSize。 // 2) 检查如果width和height都是精确值,那么就不用再根据内容进行计算所需要的width和height,跳过之后的步骤。如果有其中任何一个值不是精确值,则进入到下面计算所需长宽的步骤。 finalint widthMode = MeasureSpec.getMode(widthSpec); finalint heightMode = MeasureSpec.getMode(heightSpec); finalboolean skipMeasure = widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY; mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); if (skipMeasure || mAdapter == null) { return; } // 第二部分: // 1) 开启布局流程计算出所有Child的边界 // 2) 然后根据计算出的Child的边界计算出RecyclerView的所需width和height // 3) 检查是否需要再次测量 if (mState.mLayoutStep == State.STEP_START) { dispatchLayoutStep1(); } // set dimensions in 2nd step. Pre-layout should happen with old dimensions for // consistency mLayout.setMeasureSpecs(widthSpec, heightSpec); mState.mIsMeasuring = true; dispatchLayoutStep2();
// now we can get the width and height from the children. // 布局过程结束,根据Children中的边界信息计算并设置RecyclerView长宽的测量值 mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
// if RecyclerView has non-exact width and height and if there is at least one child // which also has non-exact width & height, we have to re-measure. // 检查是否需要再次测量。如果RecyclerView仍然有非精确的宽和高,或者这里还有至少一个Child还有非精确的宽和高,我们就需要在此测量。 if (mLayout.shouldMeasureTwice()) { mLayout.setMeasureSpecs( MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); mState.mIsMeasuring = true; dispatchLayoutStep2(); // now we can get the width and height from the children. mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec); } } else { // 第一部分:如果RecyclerView已经设置了Size固定,则执行LayoutManager#onMeasure方法 if (mHasFixedSize) { mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); return; } // 第二部分: // 1) 如果在测量的过程中有数据有更新,则先处理更新的数据 // 2) 执行自定义测量流程,这需要自定义的LayoutManager#onMeasure方法。 // custom onMeasure if (mAdapterUpdateDuringMeasure) { eatRequestLayout(); onEnterLayoutOrScroll(); processAdapterUpdatesAndSetAnimationFlags(); onExitLayoutOrScroll();
if (mState.mRunPredictiveAnimations) { mState.mInPreLayout = true; } else { // consume remaining updates to provide a consistent state with the layout pass. mAdapterHelper.consumeUpdatesInOnePass(); mState.mInPreLayout = false; } mAdapterUpdateDuringMeasure = false; resumeRequestLayout(false); } // 处理完新更新的数据,然后执行自定义测量操作。 if (mAdapter != null) { mState.mItemCount = mAdapter.getItemCount(); } else { mState.mItemCount = 0; } eatRequestLayout(); mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); resumeRequestLayout(false); mState.mInPreLayout = false; // clear } }
@Override protectedvoidonLayout(boolean changed, int l, int t, int r, int b){ TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG); dispatchLayout(); TraceCompat.endSection(); mFirstLayoutComplete = true; }
voiddispatchLayout(){ ...省略部分代码 mState.mIsMeasuring = false; if (mState.mLayoutStep == State.STEP_START) { // 1) 没有执行过布局流程的情况 dispatchLayoutStep1(); mLayout.setExactMeasureSpecsFrom(this); dispatchLayoutStep2(); } 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. // 2) 执行过布局流程,但是之后size又有变化的情况 mLayout.setExactMeasureSpecsFrom(this); dispatchLayoutStep2(); } else { // 3) 执行过布局流程,可以直接使用之前数据的情况 // always make sure we sync them (to ensure mode is exact) mLayout.setExactMeasureSpecsFrom(this); } dispatchLayoutStep3(); }
/** * The first step of a layout where we; * - process adapter updates * - decide which animation should run * - save information about current views * - If necessary, run predictive layout and save its information */ privatevoiddispatchLayoutStep1(){ mState.assertLayoutStep(State.STEP_START); mState.mIsMeasuring = false; eatRequestLayout(); mViewInfoStore.clear(); onEnterLayoutOrScroll(); processAdapterUpdatesAndSetAnimationFlags(); // 动画相关 saveFocusInfo(); mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged; mItemsAddedOrRemoved = mItemsChanged = false; mState.mInPreLayout = mState.mRunPredictiveAnimations; mState.mItemCount = mAdapter.getItemCount(); findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
if (mState.mRunSimpleAnimations) { // Step 0: Find out where all non-removed items are, pre-layout int count = mChildHelper.getChildCount(); for (int i = 0; i < count; ++i) { final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) { continue; } final ItemHolderInfo animationInfo = mItemAnimator .recordPreLayoutInformation(mState, holder, ItemAnimator.buildAdapterChangeFlagsForAnimations(holder), holder.getUnmodifiedPayloads()); mViewInfoStore.addToPreLayout(holder, animationInfo); if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved() && !holder.shouldIgnore() && !holder.isInvalid()) { long key = getChangedHolderKey(holder); // This is NOT the only place where a ViewHolder is added to old change holders // list. There is another case where: // * A VH is currently hidden but not deleted // * The hidden item is changed in the adapter // * Layout manager decides to layout the item in the pre-Layout pass (step1) // When this case is detected, RV will un-hide that view and add to the old // change holders list. mViewInfoStore.addToOldChangeHolders(key, holder); } } } if (mState.mRunPredictiveAnimations) { // Step 1: run prelayout: This will use the old positions of items. The layout manager // is expected to layout everything, even removed items (though not to add removed // items back to the container). This gives the pre-layout position of APPEARING views // which come into existence as part of the real layout.
// Save old positions so that LayoutManager can run its mapping logic. saveOldPositions(); finalboolean didStructureChange = mState.mStructureChanged; mState.mStructureChanged = false; // temporarily disable flag because we are asking for previous layout mLayout.onLayoutChildren(mRecycler, mState); mState.mStructureChanged = didStructureChange;
for (int i = 0; i < mChildHelper.getChildCount(); ++i) { final View child = mChildHelper.getChildAt(i); final ViewHolder viewHolder = getChildViewHolderInt(child); if (viewHolder.shouldIgnore()) { continue; } if (!mViewInfoStore.isInPreLayout(viewHolder)) { int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder); boolean wasHidden = viewHolder .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST); if (!wasHidden) { flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT; } final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation( mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads()); if (wasHidden) { recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo); } else { mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo); } } } // we don't process disappearing list because they may re-appear in post layout pass. clearOldPositions(); } else { clearOldPositions(); } onExitLayoutOrScroll(); resumeRequestLayout(false); mState.mLayoutStep = State.STEP_LAYOUT; }
/** * Keeps data about views to be used for animations */ final ViewInfoStore mViewInfoStore = new ViewInfoStore();
继续来看ViewInfoStore的addToPreLayout方法
/** * Adds the item information to the prelayout tracking * @param holder The ViewHolder whose information is being saved * @param info The information to save */ voidaddToPreLayout(ViewHolder holder, ItemHolderInfo info){ InfoRecord record = mLayoutHolderMap.get(holder); if (record == null) { record = InfoRecord.obtain(); mLayoutHolderMap.put(holder, record); } record.preInfo = info; record.flags |= FLAG_PRE; }
/** * The second layout step where we do the actual layout of the views for the final state. * This step might be run multiple times if necessary (e.g. measure). */ privatevoiddispatchLayoutStep2(){ eatRequestLayout(); onEnterLayoutOrScroll(); mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS); mAdapterHelper.consumeUpdatesInOnePass(); mState.mItemCount = mAdapter.getItemCount(); mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
// onLayoutChildren may have caused client code to disable item animations; re-check mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null; mState.mLayoutStep = State.STEP_ANIMATIONS; onExitLayoutOrScroll(); resumeRequestLayout(false); }
根据注释就可以看出这是我们做实际布局的逻辑。
dispatchLayoutStep3
/** * The final step of the layout where we save the information about views for animations, * trigger animations and do any necessary cleanup. */ privatevoiddispatchLayoutStep3(){ mState.assertLayoutStep(State.STEP_ANIMATIONS); eatRequestLayout(); onEnterLayoutOrScroll(); mState.mLayoutStep = State.STEP_START; if (mState.mRunSimpleAnimations) { // Step 3: Find out where things are now, and process change animations. // traverse list in reverse because we may call animateChange in the loop which may // remove the target view holder. for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) { ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); if (holder.shouldIgnore()) { continue; } long key = getChangedHolderKey(holder); final ItemHolderInfo animationInfo = mItemAnimator .recordPostLayoutInformation(mState, holder); ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key); if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) { // run a change animation
// If an Item is CHANGED but the updated version is disappearing, it creates // a conflicting case. // Since a view that is marked as disappearing is likely to be going out of // bounds, we run a change animation. Both views will be cleaned automatically // once their animations finish. // On the other hand, if it is the same view holder instance, we run a // disappearing animation instead because we are not going to rebind the updated // VH unless it is enforced by the layout manager. finalboolean oldDisappearing = mViewInfoStore.isDisappearing( oldChangeViewHolder); finalboolean newDisappearing = mViewInfoStore.isDisappearing(holder); if (oldDisappearing && oldChangeViewHolder == holder) { // run disappear animation instead of change mViewInfoStore.addToPostLayout(holder, animationInfo); } else { final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout( oldChangeViewHolder); // we add and remove so that any post info is merged. mViewInfoStore.addToPostLayout(holder, animationInfo); ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder); if (preInfo == null) { handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder); } else { animateChange(oldChangeViewHolder, holder, preInfo, postInfo, oldDisappearing, newDisappearing); } } } else { mViewInfoStore.addToPostLayout(holder, animationInfo); } }
// Step 4: Process view info lists and trigger animations mViewInfoStore.process(mViewInfoProcessCallback); }
mState.mRunPredictiveAnimations = false; mLayout.mRequestedSimpleAnimations = false; if (mRecycler.mChangedScrap != null) { mRecycler.mChangedScrap.clear(); } if (mLayout.mPrefetchMaxObservedInInitialPrefetch) { // Initial prefetch has expanded cache, so reset until next prefetch. // This prevents initial prefetches from expanding the cache permanently. mLayout.mPrefetchMaxCountObserved = 0; mLayout.mPrefetchMaxObservedInInitialPrefetch = false; mRecycler.updateViewCacheSize(); }
/** * Adds the item information to the post layout list * @param holder The ViewHolder whose information is being saved * @param info The information to save */ voidaddToPostLayout(ViewHolder holder, ItemHolderInfo info){ InfoRecord record = mLayoutHolderMap.get(holder); if (record == null) { record = InfoRecord.obtain(); mLayoutHolderMap.put(holder, record); } record.postInfo = info; record.flags |= FLAG_POST; }