看过RecyclerView源码的人都知道,他里面有个preLayout的状态,也有一个postLayout的状态,经常会搞得人云里雾里。大家都知道,preLayout阶段是用于记录数据集改变前子控件的信息,postLayout阶段是用于记录数据集改变后子空间的信息,但是具体怎样的阶段才是预布局阶段呢?下面我们追溯一下源码。
先看一下RecyclerView中的状态类中的有关预布局状态的东西。
boolean mInPreLayout = false;
public boolean isPreLayout() { return mInPreLayout; }
这个mInPreLayout默认是false,所以我们只需要找到他何时为赋值为true,就能知道预布局开始的时间。
if (mState.mRunPredictiveAnimations) { mState.mInPreLayout = true; } else { // consume remaining updates to provide a consistent state with the layout pass. mAdapterHelper.consumeUpdatesInOnePass(); mState.mInPreLayout = false; }第一处发生在onMeasure方法中。
mState.mInPreLayout = mState.mRunPredictiveAnimations;第二处发生在dispatchLayoutStep1()方法中,即在onLayout()方法中。
上述两处都和mRunPredictiveAnimations挂钩,所以我们需要找到他被赋值为true的时候。
mState.mRunSimpleAnimations = mFirstLayoutComplete && mItemAnimator != null && (mDataSetHasChangedAfterLayout || animationTypeSupported || mLayout.mRequestedSimpleAnimations) && (!mDataSetHasChangedAfterLayout || mAdapter.hasStableIds()); mState.mRunPredictiveAnimations = mState.mRunSimpleAnimations && animationTypeSupported && !mDataSetHasChangedAfterLayout && predictiveItemAnimationsEnabled();
继续追溯mRunSimpleAnimations,再追溯mFirstLayoutComplete(通过上面这块代码不难得知只有mFirstLayoutComplete才行得通)。
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG); dispatchLayout(); TraceCompat.endSection(); mFirstLayoutComplete = true; }发现又回到了这里,但是我们在dispatchLayout()方法后才让mFirstLayoutComplete = true, 所以第一次onLayout的时候,肯定不会执行预布局。所以我们明白了,preLayout并不是在第一次measure和layout前所存在的状态,而是在数据集发生变化的时候,所应该具有的状态!这一点很重要。
接下来我们看一下预布局的时候他都干了些什么,即dispatchLayoutStep1()方法中的部分代码。
先看注释
/** * 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 */1.处理适配器的更新
2.决定应该采用什么动画
3.保存当前views的信息
4.运行预布局并且保存它的信息
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); }果然就是存储了一些信息到mViewInfoStore中。
第二部分的预布局直接看注释即可:
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.
步骤1:运行prelayout:这将使用项目的旧位置。 LayoutManager将要去layout所有东西,甚至被移除的item(尽管没有把被移除的item重新添加回容器中)。
这给出了正在显示的item的预布局位置作为真实布局的一部分而存在。