MeasureSpec是一个32位的int型的数,表示一个组件的大小。这个数的前2位表示测量模式,后30位表示view的尺寸大小。
View的三种测量模式如下:
MeasureSpec.EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值(例如layout_width="100dp")、match_parent时,父容器会将其设置为EXACTLY;
MeasureSpec.AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,父容器会将其设置为AT_MOST;
MeasureSpec.UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。
对子view进行测量之前,会调取getChildMeasureSpec方法来获得子view的MeasureSpec,子view的MeasureSpec是由父容器的MeasureSpec(parentWidthMeasureSpec)还有自身的LayoutParams(lp.height和lp.width),还有View自己的Margin和Padding来确定的,下面是从ViewGroup类中找到的getChildMeasureSpec的源码:
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
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 = 0;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size.... find out how
// big it should be
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
以上代码可用一个表格来说明:
parentMeasureSpec和childMeasureSpec为什么会存在这样的转换关系?我是这样理解的:
- 当子view指定精确的大小时,无论父容器的测量模式是什么,父容器都会依据子view所要求的dimension来确定子view的大小,即子view的模式是EXACTLY。
- 当父容器为精确模式时,父容器的大小就确定了。如果子view的属性是match_parent,子view填满父容器,子view的大小就等于父容器的大小,那么子view的模式就是EXACTLY;如果子view的属性为wrap_content,那么子view的大小是不确定的,但是必须小于父容器的size,所以子view的MeasureSpec为AT_MOST+size。
- 当父容器为AT_MOST最大模式时,这是时父容器的大小不确定,但是不能大于size。此时不论子view的属性是match_parent还是wrap_content,模式都是最大模式,并且小于size。
- 当父容器为UNSPECIFIED未知模式时,此时无论子view的dimension为多大都是可以的,因为这个模式下父容器不限制子view的大小,要多大有多大。