前言
View 的工作原理中最重要的就是测量、布局、绘制三大过程,而其中测量是最复杂的;
那么我们就来介绍下View 的测量过程;
一、MeasureSpec
测量自身的大小的时候,会执行measure(int widthMeasureSpec, int heightMeasureSpec)方法。注意方法中两个参数,它们其实是一个int 类型的MeasureSpec;
1、specMode
测量模式分为三种:
- UNSPECIFIED模式:本质就是不限制模式,父视图不对子View进行任何约束,View想要多大要多大,想要多长要多长;
- EXACTLY模式:该模式其实对应的场景就是match_parent或者是一个具体的数据(50dp或80px),父视图为子View指定一个确切的大小,无论子View的值设置多大,都不能超出父视图的范围;
- AT_MOST模式:这个模式对应的场景就是wrap_content,其内容就是父视图给子View设置一个最大尺寸,子View只要不超过这个尺寸即可;
2、MeasureSpec
View的MeasureSpec值是根据子View的布局参数(LayoutParams)和父容器的MeasureSpec至计算而来的,其具体逻辑封装在了getChildMeasureSpec()方法中
public static int getChildMeasureSpec(
int spec, int padding, int childDimension) {
//1、获取parent的specMode
int specMode = MeasureSpec.getMode(spec);
//2、获取parent的specSize
int specSize = MeasureSpec.getSize(spec);
//3、size=剩余的可用大小
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
//4、通过switch语句判断parent的集中mode,分别处理
switch (specMode) {
// 5、parent为MeasureSpec.EXACTLY时
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
//5.1、当childDimension大于0时,表示child的大小是
//明确指出的,如layout_width= "100dp";
// 此时child的大小= childDimension,
resultSize = childDimension;
//child的测量模式= MeasureSpec.EXACTLY
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
//5.2、此时为LayoutParams.MATCH_PARENT
//也就是 android:layout_width="match_parent"
//因为parent的大小是明确的,child要匹配parent的大小
//那么我们就直接让child=parent的大小就好
resultSize = size;
//同样,child的测量模式= MeasureSpec.EXACTLY
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
//5.3、此时为LayoutParams.WRAP_CONTENT
//也就是 android:layout_width="wrap_content"
// 这个模式需要特别对待,child说我要的大小刚好够放
//需要展示的内容就好,而此时我们并不知道child的内容
//需要多大的地方,暂时先把parent的size给他
resultSize = size;
//自然,child的mode就是MeasureSpec.AT_MOST的了
resultMode = MeasureSpec.AT_MOST;
}
break;
// 5、parent为AT_MOST,此时child最大不能超过parent
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
//同样child大小明确时,
//大小直接时指定的childDimension
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// child要跟parent一样大,resultSize=可用大小
resultSize = size;
//因为parent是AT_MOST,child的大小也还是未定的,
//所以也是MeasureSpec.AT_MOST
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
//又是特殊情况,先给child可用的大小
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// 这种模式是很少用的,我们也看下吧
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// 与前面同样的处理
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should
// be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
//通过传入resultSize和resultMode生成一个MeasureSpec.返回
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- ①当子View采用具体数值(dp / px)时:无论父容器的测量模式是什么,子View的测量模式都是EXACTLY且大小等于设置的具体数值;
- ②当子View采用match_parent时:子View的测量模式与父容器的测量模式一致;若测量模式为EXACTLY,则子View的大小为父容器的剩余空间;若测量模式为AT_MOST,则子View的大小不超过父容器的剩余空间;
- ③当子View采用wrap_parent时:如果父容器测量模式为UNSPECIFIED,子View也为UNSPECIFIED,否则子View为AT_MOST且大小不超过父容器的剩余空间;
二、view测量过程
1、performMeasure()
加载好系统布局资源后,会触发ViewRootImpl的performTraversals()方法,该方法内容会开始执行测量、布局和绘制的工作,我们来看这个方法的源码关键部分:
private void performTraversals() {
...
if (!mStopped) {
//获取顶层布局的childWidthMeasureSpec
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
//获取顶层布局的childHeightMeasureSpec
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
//测量开始测量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
if (didLayout) {
//执行布局方法
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
}
if (!cancelDraw && !newSurface) {
...
//开始绘制了哦
performDraw();
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
整个方法内部其实就是做了一些基础的判断后,再顺序的调用测量、布局和绘制的相关方法,从而完成自定义View的整个工作流程;
performTraversals方法,使用的是getRootMeasureSpec方法来获取子View的MeasureSpec;
整个Activity的顶层View其实就是一个DecorView,所以这里获取的其实是DeorView的MeasureSpec,然后将其传入performMeasure方法中去开始测量,现在看看PerformMeasure方法:
private void performMeasure(int childWidthMeasureSpec,
int childHeightMeasureSpec) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
//mView其实就是我们的顶层DecorView,从DecorView开始测量
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
2、measure()
源码中找到了measure方法
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int oWidth = insets.left + insets.right;
int oHeight = insets.top + insets.bottom;
widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth);
heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
}
// Suppress sign extension for the low bytes
long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
final boolean forceLayout = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT;
// Optimize layout by avoiding an extra EXACTLY pass when the view is
// already measured as the correct size. In API 23 and below, this
// extra pass is required to make LinearLayout re-distribute weight.
final boolean specChanged = widthMeasureSpec != mOldWidthMeasureSpec
|| heightMeasureSpec != mOldHeightMeasureSpec;
final boolean isSpecExactly = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY
&& MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.EXACTLY;
final boolean matchesSpecSize = getMeasuredWidth() == MeasureSpec.getSize(widthMeasureSpec)
&& getMeasuredHeight() == MeasureSpec.getSize(heightMeasureSpec);
final boolean needsLayout = specChanged
&& (sAlwaysRemeasureExactly || !isSpecExactly || !matchesSpecSize);
if (forceLayout || needsLayout) {
// first clears the measured dimension flag
mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;
resolveRtlPropertiesIfNeeded();
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
long value = mMeasureCache.valueAt(cacheIndex);
// Casting a long to int drops the high 32 bits, no mask needed
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
// flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
throw new IllegalStateException("View with id " + getId() + ": "
+ getClass().getName() + "#onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
}
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
(long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
View的测量是View.onMeasure而ViewGroup的测量则是XXLayout.onMeasure,这两种onMeasure方法的实现是不同的
3、View.onMeasure()
获取一个建议最小值;
调用getDefaultSize方法定义对View尺寸的测量逻辑;
调用setMeasureDimension()储存测量后的View宽/高;
protected void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
setMeasuredDimension(
getDefaultSize(getSuggestedMinimumWidth(),
widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(),
heightMeasureSpec));
}
getSuggestedMinimumWidth()
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
前View是否有背景?没有就返回android:minWidth设置的值:有就返回android:minWidth和mBackground.getMinimumWidth()中较大的那个值;
public int getMinimumWidth() {
final int intrinsicWidth = getIntrinsicWidth();
//返回背景图Drawable的原始宽度
return intrinsicWidth > 0 ? intrinsicWidth :0 ;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
getIntrinsicWidth()获取的是背景图的原始宽度,背景图是BitmapDrawable则有原始宽度,在没有原始宽度的情况下则返回0;
4、getDefaultSize()
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
//1、获得MeasureSpec的mode
int specMode = MeasureSpec.getMode(measureSpec);
//2、获得MeasureSpec的specSize
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
//这个我们先不看他
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
//3、可以看到,最终返回的size就是我们MeasureSpec中测量得到的size
result = specSize;
break;
}
return result;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
测量模式是AT_MOST还是EXACTLY,最终返回的Size是一样的;
5、setMeasureDimension
该方法是储存测量后的View的宽和高的,在自定义View的时候,我们自己重写的onMeasure方法最后一定要调用这个方法,否则会报错
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
//1、判断是否使用视觉边界布局
boolean optical = isLayoutModeOptical(this);
//2、判断view和parentView使用的视觉边界布局是否一致
if (optical != isLayoutModeOptical(mParent)) {
//不一致时要做一些边界的处理
Insets insets = getOpticalInsets();
int opticalWidth = insets.left + insets.right;
int opticalHeight = insets.top + insets.bottom;
measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
//3、重点来了,经过过滤之后调用了setMeasuredDimensionRaw方法,看来应该是这个方法设置我们的view的大小
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
setMeasureDimensionRaw方法:
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
//最终将测量好的大小存储到mMeasuredWidth和mMeasuredHeight上,所以在测量之后
//我们可以通过调用getMeasuredWidth获得测量的宽、getMeasuredHeight获得高
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
以上就是View的测量过程,其顺序为:
performTraversals->performMeasure->measure->onMeasure-> setMeasuredDimension-> setMeasuredDimensionRaw,由setMeasuredDimensionRaw最终保存测量的数据
三、ViewGroup的测量过程详解
1、measureChildren()
作用就是遍历子View并调用measureChild()进行下一步测量
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
//参数说明:父视图的测量规格(MeasureSpec)
final int size = mChildrenCount;
final View[] children = mChildren;
//遍历所有的子view
for (int i = 0; i < size; ++i) {
final View child = children[i];
//如果View的状态不是GONE就调用measureChild()去进行下一步的测量
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
2、measureChild()
作用就是计算单个子View的MeasureSpec,调用子View的measure进行每个子View最后的宽、高的测量
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
// 获取子视图的布局参数
final LayoutParams lp = child.getLayoutParams();
// 调用getChildMeasureSpec(),根据父视图的MeasureSpec & 布局参数LayoutParams,计算单个子View的MeasureSpec
// getChildMeasureSpec()请回看上面的解析
// 获取 ChildView 的 widthMeasureSpec
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
// 获取 ChildView 的 heightMeasureSpec
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
// 将计算好的子View的MeasureSpec值传入measure(),进行最后的测量
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
3、measure()
和View的measure一致
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...
int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
// 调用onMeasure()计算视图大小
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
...
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
4、XXXLayout.onMeasure()
ViewGroup的onMeasure和View的onMeasure是不同的,究其原因其实是因为ViewGroup是一个抽象类,所以即便它继承了View也不用必须实现View中的onMeasure方法,而它的子类不具备通用的布局特性,这导致他们的子View的测量方法各不相同,因此,ViewGroup无法对onMeasure()做统一的实现
FrameLayout为例,看看它的onMeasure是如何实现的:
//这里的widthMeasureSpec、heightMeasureSpec
//其实就是我们frameLayout可用的widthMeasureSpec 、
//heightMeasureSpec
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//1、获得frameLayout下childView的个数
int count = getChildCount();
//2、看这里的代码我们可以根据前面的Measure图来进行分析,因为只要parent
//不是EXACTLY模式,以frameLayout为例,假设frameLayout本身还不是EXACTL模式,
// 那么表示他的大小此时还是不确定的,从表得知,此时frameLayout的大小是根据
//childView的最大值来设置的,这样就很好理解了,也就是childView测量好后还要再
//测量一次,因为此时frameLayout的值已经可以算出来了,对于child为MATCH_PARENT
//的,child的大小也就确定了,理解了这里,后面的代码就很 容易看懂了
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
//3、清理存储模式为MATCH_PARENT的child的队列
mMatchParentChildren.clear();
//4、下面三个值最终会用来设置frameLayout的大小
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
//5、开始便利frameLayout下的所有child
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
//6、小发现哦,只要mMeasureAllChildren是true,就算child是GONE也会被测量哦,
if (mMeasureAllChildren || child.getVisibility() != GONE) {
//7、开始测量childView
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
//8、下面代码是获取child中的width 和height的最大值,后面用来重新设置frameLayout,有需要的话
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
//9、如果frameLayout不是EXACTLY,
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
//10、存储LayoutParams.MATCH_PARENT的child,因为现在还不知道frameLayout大小,
//也就无法设置child的大小,后面需重新测量
mMatchParentChildren.add(child);
}
}
}
}
....
//11、这里开始设置frameLayout的大小
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
//12、frameLayout大小确认了,我们就需要对宽或高为LayoutParams.MATCH_PARENTchild重新测量,设置大小
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
final int width = Math.max(0, getMeasuredWidth()
- getPaddingLeftWithForeground() - getPaddingRightWithForeground()
- lp.leftMargin - lp.rightMargin);
//13、注意这里,为child是EXACTLY类型的childWidthMeasureSpec,
//也就是大小已经测量出来了不需要再测量了
//通过MeasureSpec.makeMeasureSpec生成相应的MeasureSpec
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
width, MeasureSpec.EXACTLY);
} else {
//14、如果不是,说明此时的child的MeasureSpec是EXACTLY的,直接获取child的MeasureSpec,
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
}
// 这里是对高做处理,与宽类似
final int childHeightMeasureSpec;
if (lp.height == LayoutParams.MATCH_PARENT) {
final int height = Math.max(0, getMeasuredHeight()
- getPaddingTopWithForeground() - getPaddingBottomWithForeground()
- lp.topMargin - lp.bottomMargin);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
height, MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
lp.topMargin + lp.bottomMargin,
lp.height);
}
//最终,再次测量child
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
总结
测量始于DecorView,通过不断的遍历子View的measure方法,根据ViewGroup的MeasureSpec及子View的LayoutParams来决定子View的MeasureSpec,进一步获取子View的测量宽高,然后逐层返回,不断保存ViewGroup的测量宽高;
单一View,一般重写此方法,针对wrap_content情况,规定View默认的大小值,避免于match_parent情况一致。ViewGroup,若不重写,就会执行和单子View中相同逻辑,不会测量子View。一般会重写onMeasure()方法,循环测量子View;