在RecyclerView从测量到布局的过程中必会经过的三个流程:
dispatchLayoutStep1->dispatchLayoutStep2->dispatchLayoutStep3
dispatchLayoutStep1
保存当前的子View的一些信息
mState.mInPreLayout = mState.mRunPredictiveAnimations;
mState.mItemCount = mAdapter.getItemCount();
保存当前适配器的获取焦点的item,如果该item被删除,则转移到被删除的item的前一个
saveFocusInfo
processAdapterUpdatesAndSetAnimationFlags —— 当适配器更新,决定想要执行的动画类型
- 如果你的
LayoutManager
支持predictive动画(根据你的LayoutManager
的supportsPredictiveItemAnimations
方法返回判断是否支持predictive动画),则会调用AdapterHelper的preProcess
方法来选择动画的类型,不过在这一步并不执行动画,只是预处理 - 赋值
mState.mRunSimpleAnimations
和mState.mRunPredictiveAnimations
根据上一步得到的mState.mRunSimpleAnimations,如果为true,则将RecyclerView中已存在的子View添加到ViewInfoStore中
if (mState.mRunSimpleAnimations) {
int count = mChildHelper.getChildCount();
for (int i = 0; i < count; ++i) {
...
mViewInfoStore.addToPreLayout(holder, animationInfo);
...
}
根据上一步得到的mState.mRunPredictiveAnimations,如果为true,则调用子类LayoutManager的onLayoutChildren方法,找到是否有需要消失的子View
mLayout.onLayoutChildren(mRecycler, mState);
for (int i = 0; i < mChildHelper.getChildCount(); ++i) {
...
if (wasHidden) {
recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);
} else {
mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);
}
...
}
具体可以查看下一期的RecyclerView的动画
dispatchLayoutStep2
为最终状态的子View进行真正的布局,主要依靠子类LayoutManager的onLayoutChildren
方法,以LinearLayoutManager为例:
LinearLayoutManager的onLayoutChildren方法
找到布局的锚点和方向 —— updateAnchorInfoForLayout
这个锚点代表着RecyclerView的子View从哪个位置根据得到的方向开始布局
- 如果存在待定的滚动位置或已保存的状态,则从中更新锚点信息
updateAnchorFromPendingData(state, anchorInfo)
- 通过
updateAnchorFromChildren
从RecyclerView中已添加的子View中找到锚点View
// 1. 优先考虑获得焦点的子View
final View focused = getFocusedChild();
...
// 2. 其次考虑靠近起始点和结束点的有效的子View
View referenceChild = anchorInfo.mLayoutFromEnd
? findReferenceChildClosestToEnd(recycler, state)
: findReferenceChildClosestToStart(recycler, state);
- 锚点位置根据
mStackFromEnd
选择0还是count-1
anchorInfo.assignCoordinateFromPadding();
anchorInfo.mPosition = mStackFromEnd ? state.getItemCount() - 1 : 0;
分离RecyclerView中的所有子View,并将它们添加到Scrap缓存或Cache缓存中 ——detachAndScrapAttachedViews
for (int i = childCount - 1; i >= 0; i--) {0
final View v = getChildAt(i);
scrapOrRecycleView(recycler, i, v);
}
根据第一步找锚点时得到的方向来填充
if (mAnchorInfo.mLayoutFromEnd) {
// fill towards start
updateLayoutStateToFillStart(mAnchorInfo);
fill(recycler, mLayoutState, state, false);
// fill towards end
updateLayoutStateToFillEnd(mAnchorInfo);
fill(recycler, mLayoutState, state, false);
}else{
// fill towards end
updateLayoutStateToFillEnd(mAnchorInfo);
fill(recycler, mLayoutState, state, false);
// fill towards start
updateLayoutStateToFillStart(mAnchorInfo);
fill(recycler, mLayoutState, state, false);
}
填充布局 —— fill
计算填充空间
int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
while循环通过layoutChunk
方法布局子View,直到填充空间不足
// 1. layoutState.mInfinite 表示是否还有足够的空间允许摆放View
// 2. remainingSpace > 0 表示剩余空间是否大于0
// 3. layoutState.hasMore(state) 表示当前的mCurrentPosition是否大于0,且小于mItemCount
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)){
layoutChunk(recycler, state, layoutState, layoutChunkResult);
...
if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
|| !state.isPreLayout()) {
layoutState.mAvailable -= layoutChunkResult.mConsumed;
// 每次填充完子View计算所剩的填充空间(即每次减去填充的布局所消耗的空间)
remainingSpace -= layoutChunkResult.mConsumed;
}
// 将超出边界的子View回收到Cache缓存或RecyclerViewPool中
recycleByLayoutState(recycler, layoutState);
}
添加并摆放子View —— layoutChunk
- 通过Recycler的
getViewForPosition
方法获得当前mCurrentPosition对应的itemview
View view = layoutState.next(recycler);
- 根据layoutState的状态和是否翻转决定是顺序添加子View还是倒序,addView的过程中,会判断如果这个holder是来自Scrap缓存,则会调用unScrap方法将此holder从Scrap缓存中移除
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addView(view);
} else {
addView(view, 0);
}
- 测量子View并将子View所占的高度记录到LayoutChunkResult中
measureChildWithMargins
result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
- 摆放子View,调用子View的layout方法
layoutDecoratedWithMargins(view, left, top, right, bottom);
dispatchLayoutStep3
根据dispatchLayoutStep1中得到的mState.mRunSimpleAnimations值,如果为true,则处理适配器改变的动画
if (mState.mRunSimpleAnimations) {
...
mViewInfoStore.addToPostLayout(holder, animationInfo);
...
mViewInfoStore.process(mViewInfoProcessCallback);
}
主要通过ViewInfoStore的process处理动画,具体可以查看下一期的RecyclerView的动画
回收Scrap缓存中的holder,添加到Cache缓存或RecyclerViewPool中,具体可查看ReclerView的缓存分析
mLayout.removeAndRecycleScrapInt(mRecycler);
清除一些状态
例如StateInfo的两个和动画有关的变量
mState.mRunSimpleAnimations = false;
mState.mRunPredictiveAnimations = false;
清除ViewInfoStore和焦点信息
mViewInfoStore.clear();
resetFocusInfo();
清除Scrap缓存中的mChangedScrap列表
if (mRecycler.mChangedScrap != null) {
mRecycler.mChangedScrap.clear();
}
RecyclerView的测量和布局分析
如果我们的RecyclerView在xml中的宽高如下设置:
android:layout_width="match_parent"
android:layout_height="match_parent"
测量布局的流程如下图:
![](https://img.haomeiwen.com/i1915271/645f5fe66e2af9d2.png)
如果我们的RecyclerView在xml中的宽高如下设置:
android:layout_width="wrap_content"
android:layout_height="match_parent"
只要宽高有一个为wrap_content
,测量布局的流程如下图:
![](https://img.haomeiwen.com/i1915271/ef0c1d2e35598bff.png)
网友评论