自定义ViewGroup有两个重要的方法:onMeasure和onLayout。
onLayout比较好理解,就是用来对子View进行布局,设置子View的大小和位置。通过调用子view的View.layout(l,r,t,b)来实现。
onMeasure主要的功能是用来告父View,自己所需要的宽高。可以通过调用setMeasuredDimension(int measureWidth, int measureHeight)来设置宽高。
有些情况下,自定义VIewGroup的宽高(onMeasure)和布局(onLayout) 会受子View的影响。例如子View的数量、大小等因素的影响。
子View数量比较好理解,可以调用addView的方法来添加子View。
子View的大小由两个因素决定:LayoutParams和ViewGruop 的onMeasure的参数(int widthMeasureSpec, int heightMeasureSpec)。
1、LayoutParams:子View用来告诉父View,自己要如何布局。至少包括View的宽度和高度。
把子View添加到ViewGruop里面,一定会需要一个LayoutParams,如果没有指定LayoutParams参数,例如,调用addView(View v)的方法,则ViewGruop就会赋给子View一个默认的LayoutParams,通常是一个LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)。
也可以通过xml来定义LayoutParams。使用LayoutInflater的inflate方法,可以从xml导入一个View,这个View的LayoutParams在xml中就定义好了。ViewGruop会读取xml中的内容,根据xml的定义创建一个LayoutParams。
2、widthMeasureSpec(heightMeasureSpec):widthMeasureSpec包含两部分,specMode和specWidth,具体参照如下代码:
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);
}