前言
上一篇,说了RecyclerView
的回收复用,这一篇,我们来说说RecyclerView
的绘制流程。
- 【Android进阶】RecyclerView之ItemDecoration(一)
- 【Android进阶】RecyclerView之缓存(二)
- 【Android进阶】RecyclerView之绘制流程(三)
onMeasure
我们先看看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
方法
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
方法设置宽高了
接着,是根据isAutoMeasureEnabled
为true
或false
,会走2套逻辑,通过查看源码可以发现,isAutoMeasureEnabled
即mAutoMeasure
在LayoutManager
中,默认为false
,但在LinearLayoutManager
中为true
LinearLayoutManager
相关代码
public boolean isAutoMeasureEnabled() {
return true;
}
而onMeasure
的主要逻辑也是在isAutoMeasureEnabled
为true
时,我们接着往下看
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方法。
if (this.mState.mLayoutStep == 1) {
this.dispatchLayoutStep1();
}
mLayoutStep
默认值是 State.STEP_START
即为1,关于dispatchLayoutStep1
方法,其实没有必要过多分析,因为分析源码主要是对于绘制思想的理解,如果过多的纠结于每一行代码的含义,那么会陷入很大的困扰中。执行完之后,是this.mState.mLayoutStep = 2;
即STEP_LAYOUT
状态。
接下来,是真正执行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
方法
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仍能显示出来的原因。