MeasureSpec不是唯一由LayoutParams决定的,LayoutParams需要和父容器一起才能决定View的MeasureSpec,从而进一步决定View的宽高。另外,++对于DecorView和普通View来说,MeasureSpec的转换过程略有不同。++ 对于DecorView,其MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同决定;对于普通View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定。MeasureSpec确定之后,onMeasure就可以确定View的测量宽高。
MeasureSpec遵循的规则:
-
Layou
tParams.MATCH_PARENT:精确模式,大小就是窗口或父容器的大小; -
LayoutParams.WRAP_CONTENT:最大模式,大小不定,但是不能超过窗口大小;
-
固定大小(比如100dp):精确模式,大小为LayoutParams中指定的大小。
ViewGroup的measureChildWithMargins方法
/**
-
Ask one of the children of this view to measure itself, taking into
-
account both the MeasureSpec requirements for this view and its padding
-
and margins. The child must have MarginLayoutParams The heavy lifting is
-
done in getChildMeasureSpec.
-
@param child The child to measure
-
@param parentWidthMeasureSpec The width requirements for this view
-
@param widthUsed Extra space that has been used up by the parent
-
horizontally (possibly by other children of the parent)
-
@param parentHeightMeasureSpec The height requirements for this view
-
@param heightUsed Extra space that has been used up by the parent
-
vertically (possibly by other children of the parent)
*/
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
- widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
- heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
上述方法会对子元素进行measure,在调用子元素的measure方法之前,会先通过getChildMeasureSpec方法来获取子元素的MeasureSpec。显然,子元素的MeasureSpec的创建和父容器的MeasureSpec和子元素本身的LayoutParams有关,此外还和View的margin和padding有关,具体情况如下:
/**
-
Does the hard part of measureChildren: figuring out the MeasureSpec to
-
pass to a particular child. This method figures out the right MeasureSpec
-
for one dimension (height or width) of one child view.
-
The goal is to combine information from our MeasureSpec with the
-
LayoutParams of the child to get the best possible results. For example,
-
if the this view knows its size (because its MeasureSpec has a mode of
-
EXACTLY), and the child has indicated in its LayoutParams that it wants
-
to be the same size as the parent, the parent should ask the child to
-
layout given an exact size.
-
@param spec The requirements for this view
-
@param padding The padding of this view for the current dimension and
-
margins, if applicable
-
@param childDimension How big the child wants to be in the current
-
dimension
-
@return a MeasureSpec integer for the child
*/
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 = 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;
}
//noinspection ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
find out how
// big it should be
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = MeasureSpec.UNSPECIFIED;
}
break;
}
//noinspection ResourceType
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}