上一篇,說了RecyclerView
的回收複用,這一篇,咱們來講說RecyclerView
的繪製流程。java
咱們先看看RecyclerView#onMeasure()
方法緩存
protected void onMeasure(int widthSpec, int heightSpec) {
if (this.mLayout == null) {
this.defaultOnMeasure(widthSpec, heightSpec);
} else {
if (!this.mLayout.isAutoMeasureEnabled()) {
if (this.mHasFixedSize) {
this.mLayout.onMeasure(this.mRecycler, this.mState, widthSpec, heightSpec);
return;
}
if (this.mAdapterUpdateDuringMeasure) {
this.startInterceptRequestLayout();
this.onEnterLayoutOrScroll();
this.processAdapterUpdatesAndSetAnimationFlags();
this.onExitLayoutOrScroll();
if (this.mState.mRunPredictiveAnimations) {
this.mState.mInPreLayout = true;
} else {
this.mAdapterHelper.consumeUpdatesInOnePass();
this.mState.mInPreLayout = false;
}
this.mAdapterUpdateDuringMeasure = false;
this.stopInterceptRequestLayout(false);
} else if (this.mState.mRunPredictiveAnimations) {
this.setMeasuredDimension(this.getMeasuredWidth(), this.getMeasuredHeight());
return;
}
if (this.mAdapter != null) {
this.mState.mItemCount = this.mAdapter.getItemCount();
} else {
this.mState.mItemCount = 0;
}
this.startInterceptRequestLayout();
this.mLayout.onMeasure(this.mRecycler, this.mState, widthSpec, heightSpec);
this.stopInterceptRequestLayout(false);
this.mState.mInPreLayout = false;
} else {
int widthMode = MeasureSpec.getMode(widthSpec);
int heightMode = MeasureSpec.getMode(heightSpec);
this.mLayout.onMeasure(this.mRecycler, this.mState, widthSpec, heightSpec);
boolean measureSpecModeIsExactly = widthMode == 1073741824 && heightMode == 1073741824;
if (measureSpecModeIsExactly || this.mAdapter == null) {
return;
}
if (this.mState.mLayoutStep == 1) {
this.dispatchLayoutStep1();
}
this.mLayout.setMeasureSpecs(widthSpec, heightSpec);
this.mState.mIsMeasuring = true;
this.dispatchLayoutStep2();
this.mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
if (this.mLayout.shouldMeasureTwice()) {
this.mLayout.setMeasureSpecs(MeasureSpec.makeMeasureSpec(this.getMeasuredWidth(), 1073741824), MeasureSpec.makeMeasureSpec(this.getMeasuredHeight(), 1073741824));
this.mState.mIsMeasuring = true;
this.dispatchLayoutStep2();
this.mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
}
}
}
}
複製代碼
咱們從上往下看,首先,mLayout
即爲LayoutManager
,若是其爲null
會執行defaultOnMeasure
方法ide
void defaultOnMeasure(int widthSpec, int heightSpec) {
int width = RecyclerView.LayoutManager.chooseSize(widthSpec, this.getPaddingLeft() + this.getPaddingRight(), ViewCompat.getMinimumWidth(this));
int height = RecyclerView.LayoutManager.chooseSize(heightSpec, this.getPaddingTop() + this.getPaddingBottom(), ViewCompat.getMinimumHeight(this));
this.setMeasuredDimension(width, height);
}
複製代碼
能夠看到,這裏沒有測量item
的高度就直接調用setMeasuredDimension
方法設置寬高了post
接着,是根據isAutoMeasureEnabled
爲true
或false
,會走2套邏輯,經過查看源碼能夠發現,isAutoMeasureEnabled
即mAutoMeasure
在LayoutManager
中,默認爲false
,但在LinearLayoutManager
中爲true
this
LinearLayoutManager
相關代碼spa
public boolean isAutoMeasureEnabled() {
return true;
}
複製代碼
而onMeasure
的主要邏輯也是在isAutoMeasureEnabled
爲true
時,咱們接着往下看code
int widthMode = MeasureSpec.getMode(widthSpec);
int heightMode = MeasureSpec.getMode(heightSpec);
this.mLayout.onMeasure(this.mRecycler, this.mState, widthSpec, heightSpec);
boolean measureSpecModeIsExactly = widthMode == 1073741824 && heightMode == 1073741824;
if (measureSpecModeIsExactly || this.mAdapter == null) {
return;
}
複製代碼
若是寬和高的測量值是絕對值時,直接跳過onMeasure方法。ip
if (this.mState.mLayoutStep == 1) {
this.dispatchLayoutStep1();
}
複製代碼
mLayoutStep
默認值是 State.STEP_START
即爲1,關於dispatchLayoutStep1
方法,其實沒有必要過多分析,由於分析源碼主要是對於繪製思想的理解,若是過多的糾結於每一行代碼的含義,那麼會陷入很大的困擾中。執行完以後,是this.mState.mLayoutStep = 2;
即STEP_LAYOUT
狀態。get
接下來,是真正執行LayoutManager
繪製的地方dispatchLayoutStep2
。源碼
private void dispatchLayoutStep2() {
this.startInterceptRequestLayout();
this.onEnterLayoutOrScroll();
this.mState.assertLayoutStep(6);
this.mAdapterHelper.consumeUpdatesInOnePass();
this.mState.mItemCount = this.mAdapter.getItemCount();
this.mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
this.mState.mInPreLayout = false;
this.mLayout.onLayoutChildren(this.mRecycler, this.mState);
this.mState.mStructureChanged = false;
this.mPendingSavedState = null;
this.mState.mRunSimpleAnimations = this.mState.mRunSimpleAnimations && this.mItemAnimator != null;
this.mState.mLayoutStep = 4;
this.onExitLayoutOrScroll();
this.stopInterceptRequestLayout(false);
}
複製代碼
能夠看到,RecyclerView
將item
的繪製交給了LayoutManager
,即mLayout.onLayoutChildren(this.mRecycler, this.mState);
,關於LayoutManager
將會在下一篇中詳細介紹。
這裏執行完以後,是this.mState.mLayoutStep = 4;
即STEP_ANIMATIONS
狀態。
以前也說過,onMeasure
的主要邏輯在isAutoMeasureEnabled
爲true
時,那麼爲何LayoutManager
中默認值爲false
?
若是isAutoMeasureEnabled
爲false
,item
能正常繪製嗎?讓咱們作個嘗試
咱們重寫isAutoMeasureEnabled
方法,返回false
class MyLinLayoutManager extends LinearLayoutManager {
public MyLinLayoutManager(Context context) {
super(context);
}
@Override
public boolean isAutoMeasureEnabled() {
return false;
}
}
複製代碼
而後將其設置給RecyclerView
,運行時,會發現item
還能正常顯示,這是爲何?這裏就要說是onLayout
方法
protected void onLayout(boolean changed, int l, int t, int r, int b) {
TraceCompat.beginSection("RV OnLayout");
this.dispatchLayout();
TraceCompat.endSection();
this.mFirstLayoutComplete = true;
}
複製代碼
這裏的就比較簡單了,來看看dispatchLayout
方法
void dispatchLayout() {
if (this.mAdapter == null) {
Log.e("RecyclerView", "No adapter attached; skipping layout");
} else if (this.mLayout == null) {
Log.e("RecyclerView", "No layout manager attached; skipping layout");
} else {
this.mState.mIsMeasuring = false;
if (this.mState.mLayoutStep == 1) {
this.dispatchLayoutStep1();
this.mLayout.setExactMeasureSpecsFrom(this);
this.dispatchLayoutStep2();
} else if (!this.mAdapterHelper.hasUpdates() && this.mLayout.getWidth() == this.getWidth() && this.mLayout.getHeight() == this.getHeight()) {
this.mLayout.setExactMeasureSpecsFrom(this);
} else {
this.mLayout.setExactMeasureSpecsFrom(this);
this.dispatchLayoutStep2();
}
this.dispatchLayoutStep3();
}
}
複製代碼
能夠看到,這裏將onMeasure
的主要邏輯從新執行了一遍,也解釋了以前,當咱們給RecyclerView
設置固定的寬高的時候,onMeasure
是直接跳過了執行,而子view仍能顯示出來的緣由。