深入了解RecyclerView预布局状态(preLayout)

时间:2021-01-28 17:34:04

看过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的预布局位置作为真实布局的一部分而存在。