在RecyclerView從測量到布局的過程中必會經(jīng)過的三個流程:
dispatchLayoutStep1->dispatchLayoutStep2->dispatchLayoutStep3
dispatchLayoutStep1
保存當(dāng)前的子View的一些信息
mState.mInPreLayout = mState.mRunPredictiveAnimations;
mState.mItemCount = mAdapter.getItemCount();
保存當(dāng)前適配器的獲取焦點的item,如果該item被刪除,則轉(zhuǎn)移到被刪除的item的前一個
saveFocusInfo
processAdapterUpdatesAndSetAnimationFlags —— 當(dāng)適配器更新,決定想要執(zhí)行的動畫類型
- 如果你的
LayoutManager
支持predictive動畫(根據(jù)你的LayoutManager
的supportsPredictiveItemAnimations
方法返回判斷是否支持predictive動畫)验辞,則會調(diào)用AdapterHelper的preProcess
方法來選擇動畫的類型诀豁,不過在這一步并不執(zhí)行動畫榆骚,只是預(yù)處理 - 賦值
mState.mRunSimpleAnimations
和mState.mRunPredictiveAnimations
根據(jù)上一步得到的mState.mRunSimpleAnimations,如果為true谬哀,則將RecyclerView中已存在的子View添加到ViewInfoStore中
if (mState.mRunSimpleAnimations) {
int count = mChildHelper.getChildCount();
for (int i = 0; i < count; ++i) {
...
mViewInfoStore.addToPreLayout(holder, animationInfo);
...
}
根據(jù)上一步得到的mState.mRunPredictiveAnimations,如果為true绽族,則調(diào)用子類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
為最終狀態(tài)的子View進行真正的布局,主要依靠子類LayoutManager的onLayoutChildren
方法项秉,以LinearLayoutManager為例:
LinearLayoutManager的onLayoutChildren方法
找到布局的錨點和方向 —— updateAnchorInfoForLayout
這個錨點代表著RecyclerView的子View從哪個位置根據(jù)得到的方向開始布局
- 如果存在待定的滾動位置或已保存的狀態(tài)绣溜,則從中更新錨點信息
updateAnchorFromPendingData(state, anchorInfo)
- 通過
updateAnchorFromChildren
從RecyclerView中已添加的子View中找到錨點View
// 1. 優(yōu)先考慮獲得焦點的子View
final View focused = getFocusedChild();
...
// 2. 其次考慮靠近起始點和結(jié)束點的有效的子View
View referenceChild = anchorInfo.mLayoutFromEnd
? findReferenceChildClosestToEnd(recycler, state)
: findReferenceChildClosestToStart(recycler, state);
- 錨點位置根據(jù)
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);
}
根據(jù)第一步找錨點時得到的方向來填充
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循環(huán)通過layoutChunk
方法布局子View娄蔼,直到填充空間不足
// 1. layoutState.mInfinite 表示是否還有足夠的空間允許擺放View
// 2. remainingSpace > 0 表示剩余空間是否大于0
// 3. layoutState.hasMore(state) 表示當(dāng)前的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
方法獲得當(dāng)前mCurrentPosition對應(yīng)的itemview
View view = layoutState.next(recycler);
- 根據(jù)layoutState的狀態(tài)和是否翻轉(zhuǎn)決定是順序添加子View還是倒序底哗,addView的過程中岁诉,會判斷如果這個holder是來自Scrap緩存,則會調(diào)用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跋选,調(diào)用子View的layout方法
layoutDecoratedWithMargins(view, left, top, right, bottom);
dispatchLayoutStep3
根據(jù)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);
清除一些狀態(tài)
例如StateInfo的兩個和動畫有關(guān)的變量
mState.mRunSimpleAnimations = false;
mState.mRunPredictiveAnimations = false;
清除ViewInfoStore和焦點信息
mViewInfoStore.clear();
resetFocusInfo();
清除Scrap緩存中的mChangedScrap列表
if (mRecycler.mChangedScrap != null) {
mRecycler.mChangedScrap.clear();
}
RecyclerView的測量和布局分析
如果我們的RecyclerView在xml中的寬高如下設(shè)置:
android:layout_width="match_parent"
android:layout_height="match_parent"
測量布局的流程如下圖:
如果我們的RecyclerView在xml中的寬高如下設(shè)置:
android:layout_width="wrap_content"
android:layout_height="match_parent"
只要寬高有一個為wrap_content
炼列,測量布局的流程如下圖: