/**
* 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 = 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);
}
这个Measure算法主要是算child的测量方式和大小的。主要根据两种根据:1. 自身的测量方式(MeasureSpec EXACTLY,AT_MOST,UNSPECIFIED)【这个是parent传过来的】2. 自身的大小 【这个是parent传过来的】3. 自己的padding【计算得到】4. child的大小【child的LayoutParam得到】根据以上四个推测出child的MeasureSpec 。也就是大小和测量方式。child的请求大小来确定到底child应该有多宽以及应该怎么测量。下面的逻辑比较多。1.如果当前View是Exactly。那么也就是说有了固定的大小。 那么Child如果说是一个固定的LayoutParam.width 或者 height,那么返回结果resultSize 就设置为这个固定值。 如果child是wrapContent,那么resultSize 就设置为当前这个View的工作区大小。也就是去除pad和margin 的大小。同时,测量方式设置为AT_MOST如果child是match_parent,那么由于当前View是固定大小的。那么resultSize 就设置为当前这个View的工作区大小。也就是去除pad和margin 的大小。同时,测量方式设置为EXACTLY2.如果当前View是MeasureSpec.AT_MOST。也就是说有个最大值。也是这三种情况 a.fixed 这个时候,当前View可以无限大,并且child有个固定值。那么就设置这个值吧。 这个地方,没有做越界判断,因此有时候会出现一个子View非常大,撑破了父View的情况。google在这里 算不算是一个bug呢?也就是当前的currrentView为wrap_content.但是parentView却有一个fixed value。 同时,childView也有一个fixed Value。这个时候,如果childView的大小超过了parentView。 则会出现childView显示不全的现象。 b.wrap_content 同理,传入AT_MOST.但是不超过当前View的大小。【这个是与当前View为EXACTLY的情况一样】 c.fill_parent 这种情况也非常奇怪。发生在currentView是wrap_content.而childView是fill_parent的情况。这个时候就没办法搞了。这个时候,就告诉子View你也没有固定的大小。也就是说循环依赖产生了。唯一的办法是让childView 继续wrap_content.也就是废掉这个fill_parent。从而打破了这种循环依赖。