上一篇,说了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仍能显示出来的缘由。