源码
/**
* 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);
}
这篇文章一个目的是介绍我的工具,另一个目的是讲讲一直困扰我的LayoutParam与MesureSpec
先讲讲背景
LayoutParam是子视图自己定的,有三种值:MATCH_PARENT=-1,WRAP_CONTENT=-2,第三种值大于0,那就是子视图自己定的尺寸了。这个函数的第三个参数就是子视图自己定的LayoutParam。
MeasureSpec是父视图给子视图定的,有:EXACTLY,AT_MOST,UNSPECIFIED。这个函数第一个参数就是父视图的MeasureSpce,这个MeasureSpec也是爷爷视图定的。这个函数的目的就是结合爷爷视图定的MeasureSpec与子视图定的LayoutParam,给子视图定个MeasureSpec。
这里假装读者认识makeMesureSpec这个函数。
函数的概览
请看下面的图:
这个图中红色的这一行意思是这个函数返回的内容,
紫色的这一行意思是这个函数返回的内容来自函数makeMesureSpec的返回(这里假装读者认识makeMesureSpec这个函数),
蓝色的这三行分别标记了函数makeMesureSpec的两个参数和引用。
用这个逻辑接着看,makeMesureSpec的第一个参数来自于局部变量reultSize,第二个参数来自于局部变量resultMode。
局部变量reultSize来自三种情况,getChildMeasureSpec::int,int,int:Local(函数中自定义的一个值),getChildMeasureSpec::int,int,int:Parameter3(函数的第三个参数,也就是前面提到的子视图自己定的尺寸),局部变量size。点开size可以看到:
这个局部变量size的值实际来自于函数的第一个参数,也就是父视图自己的尺寸。
至于resultMode,因为我一直以来都没怎么用过MeasureSpec.AT_MOST,MeasureSpec.EXACTLY,MeasureSpec.UNSPECIFIED,也就不讲了。