一 相關(guān)類的作用
RecyclerView具有很好的可擴展性。我們可以自定義子view的排列,可以指定動畫和分割線,另外RecyclerView還有一套自己的緩存機制。RecyclerView將不同的工作交給不同的類去處理。
LayoutoutManager
LayoutoutManager是RecyclerView中的一個抽象靜態(tài)內(nèi)部類。LayoutManager負(fù)責(zé)RecyclerView的布局,其中包含了Item View的獲取與回收。
LayoutManager中有幾個抽象方法非常重要:
onLayoutChildren(): 對RecyclerView進(jìn)行布局的入口方法。
fill(): 負(fù)責(zé)填充RecyclerView。
scrollVerticallyBy():根據(jù)手指的移動滑動一定距離,并調(diào)用fill()填充。
canScrollVertically()或canScrollHorizontally(): 判斷是否支持縱向滑動或橫向滑動。
ItemDecoration
ItemDecoration 是為了顯示每個 item 之間分隔樣式的。它的本質(zhì)實際上就是一個 Drawable。當(dāng) RecyclerView 執(zhí)行到 onDraw() 方法的時候,就會調(diào)用到他的 onDraw(),這時,如果你重寫了這個方法,就相當(dāng)于是直接在 RecyclerView 上畫了一個 Drawable 表現(xiàn)的東西。 而最后,在他的內(nèi)部還有一個叫g(shù)etItemOffsets()的方法,從字面就可以理解,他是用來偏移每個 item 視圖的。當(dāng)我們在每個 item 視圖之間強行插入繪畫了一段 Drawable,那么如果再照著原本的邏輯去繪 item 視圖,就會覆蓋掉 Decoration 了,所以需要getItemOffsets()這個方法,讓每個 item 往后面偏移一點,不要覆蓋到之前畫上的分隔樣式了。
ItemAnimator
每一個 item 在特定情況下都會執(zhí)行的動畫。說是特定情況,其實就是在視圖發(fā)生改變,我們手動調(diào)用notifyxxxx()的時候。通常這個時候我們會要傳一個下標(biāo),那么從這個標(biāo)記開始一直到結(jié)束,所有 item 視圖都會被執(zhí)行一次這個動畫。
Adapter
首先是適配器,適配器的作用都是類似的,用于提供每個 item 視圖,并返回給 RecyclerView 作為其子布局添加到內(nèi)部。
但是,與 ListView 不同的是,ListView 的適配器是直接返回一個 View,將這個 View 加入到 ListView 內(nèi)部。而 RecyclerView 是返回一個 ViewHolder 并且不是直接將這個 holder 加入到視圖內(nèi)部,而是加入到一個緩存區(qū)域,在視圖需要的時候去緩存區(qū)域找到 holder 再間接的找到 holder 包裹的 View。
ViewHolder
每個 ViewHolder 的內(nèi)部是一個 View,并且 ViewHolder 必須繼承自RecyclerView.ViewHolder類。 這主要是因為 RecyclerView 內(nèi)部的緩存結(jié)構(gòu)并不是像 ListView 那樣去緩存一個 View,而是直接緩存一個 ViewHolder ,在 ViewHolder 的內(nèi)部又持有了一個 View。既然是緩存一個 ViewHolder,那么當(dāng)然就必須所有的 ViewHolder 都繼承同一個類才能做到了。
二 繪制流程
RecyclerView的聲明如下:
public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2 {}
可見,RecyclerView本質(zhì)還是一個ViewGroup,那么其繪制流程肯定遵循view的繪制流程:onMeasure - onLayout - onDraw。那么就去看看這幾個方法里做了些什么。
onMeasure
首先要了解下mLayout是什么,它是一個LayoutManager,RecyclerView中的一個成員變量,通過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();
}
可以看到,我們在使用RecyclerView中,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時,skipMeasure為true,那么接下來直接return **/
final boolean measureSpecModeIsExactly =
widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
if (measureSpecModeIsExactly || mAdapter == null) {
return;
}
/** 5.mState.mLayoutStep為STEP_START,這里會執(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();這個方法很重要,下面繼續(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時,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機制,否則就執(zhí)行LayoutManager的實現(xiàn)的onMeasure方法。LinearLayoutManager的這個參數(shù)為true。
3.mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);的作用
public void onMeasure(Recycler recycler, State state, int widthSpec, int heightSpec) {
mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
}
驗證了第二步,這里執(zhí)行mRecyclerView的defaultOnMeasure(widthSpec, heightSpec);這個方法最后會執(zhí)行setMeasuredDimension(width, height);
4.measureSpecModeIsExactly 為true時,為什么直接return了?
final boolean measureSpecModeIsExactly =
widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
當(dāng)widthMode和heightMode為EXACTLY時,measureSpecModeIsExactly 為true,這個時候,onMeasure可以直接返回了。為什么呢?因為當(dāng)widthMode和heightMode都為EXACTLY時,說明RecyclerView的寬高都是固定的,不用根據(jù)子view的寬高來確定自身的寬高,所以執(zhí)行完第3步,其實RecyclerView就已經(jīng)確定大小了。
5.dispatchLayoutStep1()和dispatchLayoutStep2()
其實還有個dispatchLayoutStep3(),這三個方法才是RecyclerView的繪制流程。下面是三個方法的具體作用:
dispatchLayoutStep1: Adapter的更新; 決定該啟動哪種動畫; 保存當(dāng)前View的信息(getLeft(), getRight(), getTop(), getBottom()等); 如果有必要,先跑一次布局并將信息保存下來。
dispatchLayoutStep2: 真正對子View做布局的地方。
dispatchLayoutStep3: 為動畫保存View的相關(guān)信息; 觸發(fā)動畫; 相應(yīng)的清理工作。
這里還牽扯到了mState.mLayoutStep,這個變量在每個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的值來確定我們處于哪個狀態(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();
}
這里會根據(jù)mState.mLayoutStep判斷是否要執(zhí)行dispatchLayoutStep1();和dispatchLayoutStep2();方法,最后調(diào)用dispatchLayoutStep3();。
在onMeasure和onLayout過程中,RecyclerView將大部分的邏輯都交給了dispatchLayoutStep1()、dispatchLayoutStep2()和dispatchLayoutStep3()處理,那么就針對這三個方法分析一下。
dispatchLayoutStep1()
/**
* The first step of a layout where we;
* - process adapter updates (處理Adapter更新)
* - decide which animation should run (決定執(zhí)行哪個動畫)
* - save information about current views (保存當(dāng)前view的一些信息)
* - If necessary, run predictive layout and save its information
*/
private void dispatchLayoutStep1() {
.......
/**處理Adapter和動畫相關(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;
/**對子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有動畫時,才可能執(zhí)行兩個if語句里的代碼。如果mState.mRunPredictiveAnimations為true,會先調(diào)用一次mLayout.onLayoutChildren(mRecycler, mState);。這個方法等下講解。
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()貌似很簡單,沒做什么事情。其實不然,它調(diào)用了mLayout.onLayoutChildren(mRecycler, mState);方法,這個方法才是重頭戲。RecyclerView將子View的繪制交給了LayoutManager去做,其實就是在onLayoutChildren這個方法里處理的。
onLayoutChildren方法在子類實現(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;
// 計算錨點的位置和偏移量
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;
}
}
......
}
這個方法實在是復(fù)雜,即使刪掉了部分代碼還是有點長,但是我們只關(guān)心子view是如何繪制的。
這里有個錨點的概念。理解為要開始繪制的位置就可以了。

一開始我對這張圖有點懵逼。其實抓住那個小紅點就可以了,小紅點就是所謂的錨點,也就是繪制的開始位置。第一種情況中,小紅點在最底部,那么就從n-1的位置開始繪制子view。第二種情況中,小紅點在頂部,從0開始繪制子view。
這張圖就描述了onLayoutChildren里的主要工作。
updateAnchorInfoForLayout和updateLayoutStateToFillEnd等幾個類似的方法都是對錨點位置的計算。那么哪個方法真正執(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,這個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);
}
}
/**對子veiw進(jìn)行一次測量**/
measureChildWithMargins(view, 0, 0);
result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
/**這四個變量是子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;
}
}
/**對子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();
}
可見,我們終于找到了對子view進(jìn)行measure和layout的地方。
這里總結(jié)下dispatchLayoutStep2()方法中做的事:
計算錨點,以錨點開始填充RecyclerView(其實就是執(zhí)行fill方法)。
執(zhí)行fill方法,判斷RecyclerView是否還有空間,如果有,執(zhí)行l(wèi)ayoutChunk方法,直至填充滿。
layoutChunk方法中,尋找到當(dāng)前要添加的子view,add到RecyclerView中。
對子view進(jìn)行measure和layout。
至此,dispatchLayoutStep2()就結(jié)束了。
dispatchLayoutStep3()
onLayout中會調(diào)用dispatchLayoutStep3()方法。
private void dispatchLayoutStep3() {
......
/**重置了mState.mLayoutStep**/
mState.mLayoutStep = State.STEP_START;
/**處理動畫**/
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();
}
其實dispatchLayoutStep3()就是做了一些收尾工作,將一些變量重置,處理下動畫。
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);
}
}
看到這么簡單的方法真是開心。這里的邏輯確實很簡單,還記得ItemDecoration嗎?我們一般用這個來實現(xiàn)分割線效果,如果我們執(zhí)行了addItemDecoration,這里RecyclerView的onDraw方法就幫我們把這些分割線繪制出來。
三、總結(jié)
1.RecyclerView是將繪制流程交給LayoutManager處理,如果沒有設(shè)置不會繪制子View。
2.繪制是先確定錨點,以錨點開始,向上繪制,向下繪制,fill()至少會執(zhí)行兩次,如果繪制完還有剩余空間,則會再執(zhí)行一次fill()方法。
3.fill方法中會根據(jù)剩余空間循環(huán)執(zhí)行l(wèi)ayoutChunk方法。
4.layoutChunk方法add了子view,并對子view進(jìn)行了measure和layout。