一 相關(guān)類的作用
RecyclerView具有很好的可擴(kuò)展性。我們可以自定義子view的排列,可以指定動(dòng)畫和分割線,另外RecyclerView還有一套自己的緩存機(jī)制。RecyclerView將不同的工作交給不同的類去處理。
LayoutoutManager
LayoutoutManager是RecyclerView中的一個(gè)抽象靜態(tài)內(nèi)部類。LayoutManager負(fù)責(zé)RecyclerView的布局,其中包含了Item View的獲取與回收。
LayoutManager中有幾個(gè)抽象方法非常重要:
onLayoutChildren(): 對(duì)RecyclerView進(jìn)行布局的入口方法。
fill(): 負(fù)責(zé)填充RecyclerView。
scrollVerticallyBy():根據(jù)手指的移動(dòng)滑動(dòng)一定距離,并調(diào)用fill()填充。
canScrollVertically()或canScrollHorizontally(): 判斷是否支持縱向滑動(dòng)或橫向滑動(dòng)。
ItemDecoration
ItemDecoration 是為了顯示每個(gè) item 之間分隔樣式的。它的本質(zhì)實(shí)際上就是一個(gè) Drawable。當(dāng) RecyclerView 執(zhí)行到 onDraw() 方法的時(shí)候,就會(huì)調(diào)用到他的 onDraw(),這時(shí),如果你重寫了這個(gè)方法,就相當(dāng)于是直接在 RecyclerView 上畫了一個(gè) Drawable 表現(xiàn)的東西。 而最后,在他的內(nèi)部還有一個(gè)叫g(shù)etItemOffsets()的方法,從字面就可以理解,他是用來偏移每個(gè) item 視圖的。當(dāng)我們?cè)诿總€(gè) item 視圖之間強(qiáng)行插入繪畫了一段 Drawable,那么如果再照著原本的邏輯去繪 item 視圖,就會(huì)覆蓋掉 Decoration 了,所以需要getItemOffsets()這個(gè)方法,讓每個(gè) item 往后面偏移一點(diǎn),不要覆蓋到之前畫上的分隔樣式了。
ItemAnimator
每一個(gè) item 在特定情況下都會(huì)執(zhí)行的動(dòng)畫。說是特定情況,其實(shí)就是在視圖發(fā)生改變,我們手動(dòng)調(diào)用notifyxxxx()的時(shí)候。通常這個(gè)時(shí)候我們會(huì)要傳一個(gè)下標(biāo),那么從這個(gè)標(biāo)記開始一直到結(jié)束,所有 item 視圖都會(huì)被執(zhí)行一次這個(gè)動(dòng)畫。
Adapter
首先是適配器,適配器的作用都是類似的,用于提供每個(gè) item 視圖,并返回給 RecyclerView 作為其子布局添加到內(nèi)部。
但是,與 ListView 不同的是,ListView 的適配器是直接返回一個(gè) View,將這個(gè) View 加入到 ListView 內(nèi)部。而 RecyclerView 是返回一個(gè) ViewHolder 并且不是直接將這個(gè) holder 加入到視圖內(nèi)部,而是加入到一個(gè)緩存區(qū)域,在視圖需要的時(shí)候去緩存區(qū)域找到 holder 再間接的找到 holder 包裹的 View。
ViewHolder
每個(gè) ViewHolder 的內(nèi)部是一個(gè) View,并且 ViewHolder 必須繼承自RecyclerView.ViewHolder類。 這主要是因?yàn)?RecyclerView 內(nèi)部的緩存結(jié)構(gòu)并不是像 ListView 那樣去緩存一個(gè) View,而是直接緩存一個(gè) ViewHolder ,在 ViewHolder 的內(nèi)部又持有了一個(gè) View。既然是緩存一個(gè) ViewHolder,那么當(dāng)然就必須所有的 ViewHolder 都繼承同一個(gè)類才能做到了。
二 繪制流程
RecyclerView的聲明如下:
public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2 {}
可見,RecyclerView本質(zhì)還是一個(gè)ViewGroup,那么其繪制流程肯定遵循view的繪制流程:onMeasure - onLayout - onDraw。那么就去看看這幾個(gè)方法里做了些什么。
onMeasure
首先要了解下mLayout是什么,它是一個(gè)LayoutManager,RecyclerView中的一個(gè)成員變量,通過setLayoutManager方法賦值:
public void setLayoutManager(LayoutManager layout) {
if (layout == mLayout) {
return;
}
.......
mLayout = layout;
if (layout != null) {
if (layout.mRecyclerView != null) {
throw new IllegalArgumentException("LayoutManager " + layout
+ " is already attached to a RecyclerView:"
+ layout.mRecyclerView.exceptionLabel());
}
mLayout.setRecyclerView(this);
if (mIsAttached) {
mLayout.dispatchAttachedToWindow(this);
}
}
mRecycler.updateViewCacheSize();
requestLayout();
}
可以看到,我們?cè)谑褂肦ecyclerView中,setLayoutManager就是把我們自定義的LayoutManager傳遞給了mLayout。
回到正題,下面是RecyclerView中onMeasure的源碼:
protected void onMeasure(int widthSpec, int heightSpec) {
/** 1.判斷mLayout是否為null,為null執(zhí)行defaultOnMeasure,return **/
if (mLayout == null) {
defaultOnMeasure(widthSpec, heightSpec);
return;
}
/** 2.isAutoMeasureEnabled()一般都是true **/
if (mLayout.isAutoMeasureEnabled()) {
final int widthMode = MeasureSpec.getMode(widthSpec);
final int heightMode = MeasureSpec.getMode(heightSpec);
/** 3.調(diào)用了LayoutManager的onMeasure **/
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
/** 4.當(dāng)RecyclerView的寬高都是EXACTLY時(shí),skipMeasure為true,那么接下來直接return **/
final boolean measureSpecModeIsExactly =
widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
if (measureSpecModeIsExactly || mAdapter == null) {
return;
}
/** 5.mState.mLayoutStep為STEP_START,這里會(huì)執(zhí)行dispatchLayoutStep1(); **/
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;
/** 6.執(zhí)行dispatchLayoutStep2();這個(gè)方法很重要,下面繼續(xù)分析 **/
dispatchLayoutStep2();
// now we can get the width and height from the children.
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.
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 {
if (mHasFixedSize) {
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
return;
}
// custom onMeasure
if (mAdapterUpdateDuringMeasure) {
startInterceptRequestLayout();
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;
stopInterceptRequestLayout(false);
} else if (mState.mRunPredictiveAnimations) {
// If mAdapterUpdateDuringMeasure is false and mRunPredictiveAnimations is true:
// this means there is already an onMeasure() call performed to handle the pending
// adapter change, two onMeasure() calls can happen if RV is a child of LinearLayout
// with layout_width=MATCH_PARENT. RV cannot call LM.onMeasure() second time
// because getViewForPosition() will crash when LM uses a child to measure.
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
return;
}
if (mAdapter != null) {
mState.mItemCount = mAdapter.getItemCount();
} else {
mState.mItemCount = 0;
}
startInterceptRequestLayout();
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
stopInterceptRequestLayout(false);
mState.mInPreLayout = false; // clear
}
}
代碼里注釋了重要的步驟,應(yīng)該都挺好理解的。下面詳細(xì)分析下。
1.mLayout為null時(shí),defaultOnMeasure(widthSpec, heightSpec);做了什么?
/**
* An implementation of {@link View#onMeasure(int, int)} to fall back to in various scenarios
* where this RecyclerView is otherwise lacking better information.
*/
void defaultOnMeasure(int widthSpec, int heightSpec) {
// calling LayoutManager here is not pretty but that API is already public and it is better
// than creating another method since this is internal.
final int width = LayoutManager.chooseSize(widthSpec,
getPaddingLeft() + getPaddingRight(),
ViewCompat.getMinimumWidth(this));
final int height = LayoutManager.chooseSize(heightSpec,
getPaddingTop() + getPaddingBottom(),
ViewCompat.getMinimumHeight(this));
setMeasuredDimension(width, height);
}
mLayout為null說明我們沒有指定RecyclerView的子view如何布局和繪制,那么這里就是很常規(guī)的根據(jù)SpecMode設(shè)置RecyclerView的寬高,其它什么都沒干,這也是我們不調(diào)用setLayoutManager方法導(dǎo)致RecyclerView空白的效果。
2.isAutoMeasureEnabled()
/**Returns whether the measuring pass of layout should use the AutoMeasure mechanism of
{@link RecyclerView} or if it should be done by the LayoutManager's implementation of
{@link LayoutManager#onMeasure(Recycler, State, int, int)}.
*/
public boolean isAutoMeasureEnabled() {
return mAutoMeasure;
}
也就是說,返回true,那么執(zhí)行RecyclerView的autoMeasure機(jī)制,否則就執(zhí)行LayoutManager的實(shí)現(xiàn)的onMeasure方法。LinearLayoutManager的這個(gè)參數(shù)為true。
3.mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);的作用
public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
}
驗(yàn)證了第二步,這里執(zhí)行mRecyclerView的defaultOnMeasure(widthSpec, heightSpec);這個(gè)方法最后會(huì)執(zhí)行setMeasuredDimension(width, height);
4.measureSpecModeIsExactly 為true時(shí),為什么直接return了?
final boolean measureSpecModeIsExactly =
widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
當(dāng)widthMode和heightMode為EXACTLY時(shí),measureSpecModeIsExactly 為true,這個(gè)時(shí)候,onMeasure可以直接返回了。為什么呢?因?yàn)楫?dāng)widthMode和heightMode都為EXACTLY時(shí),說明RecyclerView的寬高都是固定的,不用根據(jù)子view的寬高來確定自身的寬高,所以執(zhí)行完第3步,其實(shí)RecyclerView就已經(jīng)確定大小了。
5.dispatchLayoutStep1()和dispatchLayoutStep2()
其實(shí)還有個(gè)dispatchLayoutStep3(),這三個(gè)方法才是RecyclerView的繪制流程。下面是三個(gè)方法的具體作用:
dispatchLayoutStep1: Adapter的更新; 決定該啟動(dòng)哪種動(dòng)畫; 保存當(dāng)前View的信息(getLeft(), getRight(), getTop(), getBottom()等); 如果有必要,先跑一次布局并將信息保存下來。
dispatchLayoutStep2: 真正對(duì)子View做布局的地方。
dispatchLayoutStep3: 為動(dòng)畫保存View的相關(guān)信息; 觸發(fā)動(dòng)畫; 相應(yīng)的清理工作。
這里還牽扯到了mState.mLayoutStep,這個(gè)變量在每個(gè)step值都是不一樣的,具體表現(xiàn)如下:
初始化為STEP_START
執(zhí)行完dispatchLayoutStep1后,mState.mLayoutStep = State.STEP_LAYOUT;
執(zhí)行完dispatchLayoutStep2后,mState.mLayoutStep = State.STEP_ANIMATIONS;
執(zhí)行完dispatchLayoutStep3后,mState.mLayoutStep = State.STEP_LAYOUT;
所以我們可以根據(jù)mState.mLayoutStep的值來確定我們處于哪個(gè)狀態(tài)。
onLayout
接下來分析onLayout方法。
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();才是處理邏輯的地方。
void dispatchLayout() {
if (mAdapter == null) {
Log.e(TAG, "No adapter attached; skipping layout");
// leave the state in START
return;
}
if (mLayout == null) {
Log.e(TAG, "No layout manager attached; skipping layout");
// leave the state in START
return;
}
mState.mIsMeasuring = false;
if (mState.mLayoutStep == State.STEP_START) {
dispatchLayoutStep1();
mLayout.setExactMeasureSpecsFrom(this);
dispatchLayoutStep2();
} else if (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.
mLayout.setExactMeasureSpecsFrom(this);
dispatchLayoutStep2();
} else {
// always make sure we sync them (to ensure mode is exact)
mLayout.setExactMeasureSpecsFrom(this);
}
dispatchLayoutStep3();
}
這里會(huì)根據(jù)mState.mLayoutStep判斷是否要執(zhí)行dispatchLayoutStep1();和dispatchLayoutStep2();方法,最后調(diào)用dispatchLayoutStep3();。
在onMeasure和onLayout過程中,RecyclerView將大部分的邏輯都交給了dispatchLayoutStep1()、dispatchLayoutStep2()和dispatchLayoutStep3()處理,那么就針對(duì)這三個(gè)方法分析一下。
dispatchLayoutStep1()
/**
* The first step of a layout where we;
* - process adapter updates (處理Adapter更新)
* - decide which animation should run (決定執(zhí)行哪個(gè)動(dòng)畫)
* - save information about current views (保存當(dāng)前view的一些信息)
* - If necessary, run predictive layout and save its information
*/
private void dispatchLayoutStep1() {
.......
/**處理Adapter和動(dòng)畫相關(guān)**/
processAdapterUpdatesAndSetAnimationFlags();
.......
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);
mViewInfoStore.addToOldChangeHolders(key, holder);
}
}
}
if (mState.mRunPredictiveAnimations) {
saveOldPositions();
final boolean didStructureChange = mState.mStructureChanged;
mState.mStructureChanged = false;
/**對(duì)子view進(jìn)行布局**/
// 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();
stopInterceptRequestLayout(false);
/**這里改變了mState.mLayoutStep的值**/
mState.mLayoutStep = State.STEP_LAYOUT;
}
當(dāng)我們的RecyclerView有動(dòng)畫時(shí),才可能執(zhí)行兩個(gè)if語句里的代碼。如果mState.mRunPredictiveAnimations為true,會(huì)先調(diào)用一次mLayout.onLayoutChildren(mRecycler, mState);。這個(gè)方法等下講解。
dispatchLayoutStep2()
private void dispatchLayoutStep2() {
startInterceptRequestLayout();
onEnterLayoutOrScroll();
mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);
mAdapterHelper.consumeUpdatesInOnePass();
mState.mItemCount = mAdapter.getItemCount();
mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
mState.mInPreLayout = false;
mLayout.onLayoutChildren(mRecycler, mState);
mState.mStructureChanged = false;
mPendingSavedState = null;
// onLayoutChildren may have caused client code to disable item animations; re-check
mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;
/**這里改變了mState.mLayoutStep的值**/
mState.mLayoutStep = State.STEP_ANIMATIONS;
onExitLayoutOrScroll();
stopInterceptRequestLayout(false);
}
dispatchLayoutStep2()貌似很簡(jiǎn)單,沒做什么事情。其實(shí)不然,它調(diào)用了mLayout.onLayoutChildren(mRecycler, mState);方法,這個(gè)方法才是重頭戲。RecyclerView將子View的繪制交給了LayoutManager去做,其實(shí)就是在onLayoutChildren這個(gè)方法里處理的。
onLayoutChildren方法在子類實(shí)現(xiàn)。那么來看看LinearLayoutManager里的onLayoutChildren方法。
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
......
//判斷繪制方向,給mShouldReverseLayout賦值,默認(rèn)是正向繪制,則mShouldReverseLayout是false
resolveShouldLayoutReverse();
final View focused = getFocusedChild();
if (!mAnchorInfo.mValid || mPendingScrollPosition != NO_POSITION
|| mPendingSavedState != null) {
mAnchorInfo.reset();
mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd;
// 計(jì)算錨點(diǎn)的位置和偏移量
updateAnchorInfoForLayout(recycler, state, mAnchorInfo);
mAnchorInfo.mValid = true;
} else if (focused != null && (mOrientationHelper.getDecoratedStart(focused)
>= mOrientationHelper.getEndAfterPadding()
|| mOrientationHelper.getDecoratedEnd(focused)
<= mOrientationHelper.getStartAfterPadding())) {
mAnchorInfo.assignFromViewAndKeepVisibleRect(focused, getPosition(focused));
}
......
if (mAnchorInfo.mLayoutFromEnd) {
......
} else {
// fill towards end
updateLayoutStateToFillEnd(mAnchorInfo);
mLayoutState.mExtra = extraForEnd;
fill(recycler, mLayoutState, state, false);
endOffset = mLayoutState.mOffset;
final int lastElement = mLayoutState.mCurrentPosition;
if (mLayoutState.mAvailable > 0) {
extraForStart += mLayoutState.mAvailable;
}
// fill towards start
updateLayoutStateToFillStart(mAnchorInfo);
mLayoutState.mExtra = extraForStart;
mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
fill(recycler, mLayoutState, state, false);
startOffset = mLayoutState.mOffset;
if (mLayoutState.mAvailable > 0) {
extraForEnd = mLayoutState.mAvailable;
// start could not consume all it should. add more items towards end
updateLayoutStateToFillEnd(lastElement, endOffset);
mLayoutState.mExtra = extraForEnd;
fill(recycler, mLayoutState, state, false);
endOffset = mLayoutState.mOffset;
}
}
......
}
這個(gè)方法實(shí)在是復(fù)雜,即使刪掉了部分代碼還是有點(diǎn)長,但是我們只關(guān)心子view是如何繪制的。
這里有個(gè)錨點(diǎn)的概念。理解為要開始繪制的位置就可以了。

一開始我對(duì)這張圖有點(diǎn)懵逼。其實(shí)抓住那個(gè)小紅點(diǎn)就可以了,小紅點(diǎn)就是所謂的錨點(diǎn),也就是繪制的開始位置。第一種情況中,小紅點(diǎn)在最底部,那么就從n-1的位置開始繪制子view。第二種情況中,小紅點(diǎn)在頂部,從0開始繪制子view。
這張圖就描述了onLayoutChildren里的主要工作。
updateAnchorInfoForLayout和updateLayoutStateToFillEnd等幾個(gè)類似的方法都是對(duì)錨點(diǎn)位置的計(jì)算。那么哪個(gè)方法真正執(zhí)行了繪制工作呢?答案是fill()方法。
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
......
int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
/**判斷是否還有空間,循環(huán)執(zhí)行l(wèi)ayoutChunk方法**/
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
layoutChunkResult.resetInternal();
......
layoutChunk(recycler, state, layoutState, layoutChunkResult);
......
if (layoutChunkResult.mFinished) {
break;
}
layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
|| !state.isPreLayout()) {
layoutState.mAvailable -= layoutChunkResult.mConsumed;
remainingSpace -= layoutChunkResult.mConsumed;
}
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
if (layoutState.mAvailable < 0) {
layoutState.mScrollingOffset += layoutState.mAvailable;
}
recycleByLayoutState(recycler, layoutState);
}
if (stopOnFocusable && layoutChunkResult.mFocusable) {
break;
}
}
return start - layoutState.mAvailable;
}
繼續(xù)找layoutChunk()方法:
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
/**尋找要繪制的view,這個(gè)next方法可是相當(dāng)重要的,里面包含RecyclerView的緩存邏輯,以后再分析吧**/
View view = layoutState.next(recycler);
if (view == null) {
return;
}
LayoutParams params = (LayoutParams) view.getLayoutParams();
/**將子view add到RecyclerView中**/
if (layoutState.mScrapList == null) {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addView(view);
} else {
addView(view, 0);
}
} else {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addDisappearingView(view);
} else {
addDisappearingView(view, 0);
}
}
/**對(duì)子veiw進(jìn)行一次測(cè)量**/
measureChildWithMargins(view, 0, 0);
result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
/**這四個(gè)變量是子view的邊距**/
int left, top, right, bottom;
if (mOrientation == VERTICAL) {
if (isLayoutRTL()) {
right = getWidth() - getPaddingRight();
left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
} else {
left = getPaddingLeft();
right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
}
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
bottom = layoutState.mOffset;
top = layoutState.mOffset - result.mConsumed;
} else {
top = layoutState.mOffset;
bottom = layoutState.mOffset + result.mConsumed;
}
} else {
top = getPaddingTop();
bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
right = layoutState.mOffset;
left = layoutState.mOffset - result.mConsumed;
} else {
left = layoutState.mOffset;
right = layoutState.mOffset + result.mConsumed;
}
}
/**對(duì)子view進(jìn)行l(wèi)ayout**/
layoutDecoratedWithMargins(view, left, top, right, bottom);
// Consume the available space if the view is not removed OR changed
if (params.isItemRemoved() || params.isItemChanged()) {
result.mIgnoreConsumed = true;
}
result.mFocusable = view.hasFocusable();
}
可見,我們終于找到了對(duì)子view進(jìn)行measure和layout的地方。
這里總結(jié)下dispatchLayoutStep2()方法中做的事:
計(jì)算錨點(diǎn),以錨點(diǎn)開始填充RecyclerView(其實(shí)就是執(zhí)行fill方法)。
執(zhí)行fill方法,判斷RecyclerView是否還有空間,如果有,執(zhí)行l(wèi)ayoutChunk方法,直至填充滿。
layoutChunk方法中,尋找到當(dāng)前要添加的子view,add到RecyclerView中。
對(duì)子view進(jìn)行measure和layout。
至此,dispatchLayoutStep2()就結(jié)束了。
dispatchLayoutStep3()
onLayout中會(huì)調(diào)用dispatchLayoutStep3()方法。
private void dispatchLayoutStep3() {
......
/**重置了mState.mLayoutStep**/
mState.mLayoutStep = State.STEP_START;
/**處理動(dòng)畫**/
if (mState.mRunSimpleAnimations) {
......
}
mLayout.removeAndRecycleScrapInt(mRecycler);
mState.mPreviousLayoutItemCount = mState.mItemCount;
mDataSetHasChangedAfterLayout = false;
mDispatchItemsChangedEvent = false;
mState.mRunSimpleAnimations = false;
mState.mRunPredictiveAnimations = false;
mLayout.mRequestedSimpleAnimations = false;
if (mRecycler.mChangedScrap != null) {
mRecycler.mChangedScrap.clear();
}
if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {
mLayout.mPrefetchMaxCountObserved = 0;
mLayout.mPrefetchMaxObservedInInitialPrefetch = false;
mRecycler.updateViewCacheSize();
}
/**表明layout過程結(jié)束**/
mLayout.onLayoutCompleted(mState);
onExitLayoutOrScroll();
stopInterceptRequestLayout(false);
mViewInfoStore.clear();
if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) {
dispatchOnScrolled(0, 0);
}
recoverFocusFromState();
resetFocusInfo();
}
其實(shí)dispatchLayoutStep3()就是做了一些收尾工作,將一些變量重置,處理下動(dòng)畫。
onDraw
public void onDraw(Canvas c) {
super.onDraw(c);
final int count = mItemDecorations.size();
for (int i = 0; i < count; i++) {
mItemDecorations.get(i).onDraw(c, this, mState);
}
}
看到這么簡(jiǎn)單的方法真是開心。這里的邏輯確實(shí)很簡(jiǎn)單,還記得ItemDecoration嗎?我們一般用這個(gè)來實(shí)現(xiàn)分割線效果,如果我們執(zhí)行了addItemDecoration,這里RecyclerView的onDraw方法就幫我們把這些分割線繪制出來。
三、總結(jié)
1.RecyclerView是將繪制流程交給LayoutManager處理,如果沒有設(shè)置不會(huì)繪制子View。
2.繪制是先確定錨點(diǎn),以錨點(diǎn)開始,向上繪制,向下繪制,fill()至少會(huì)執(zhí)行兩次,如果繪制完還有剩余空間,則會(huì)再執(zhí)行一次fill()方法。
3.fill方法中會(huì)根據(jù)剩余空間循環(huán)執(zhí)行l(wèi)ayoutChunk方法。
4.layoutChunk方法add了子view,并對(duì)子view進(jìn)行了measure和layout。