自己的一点理解:
其实measureSpec就是view的一个内部类,封装了这个view对象的Mode和Size,封装在一个Int类型中,4字节*8位=32位,前2位封装的mode,后30位封装的size
推荐的两篇博客地址:
先说下结论,onMeasure是测量view或viewGroup时系统调用的,它的参数widthMeasureSpec和heightMeasureSpec既不代表父ViewGroup的宽高也不代表子View的宽高,它表示了父ViewGroup对自己的期望,至于子View实际的宽高得看setMeasuredDimension传的是什么。
class MyView:View {
constructor(context: Context,attrs:AttributeSet):super(context,attrs){}
constructor(context: Context):super(context){}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
var myWidth = 0
var myHeight = 0
var defaultValue = 50
var widthMode = MeasureSpec.getMode(widthMeasureSpec)
var heightMode = MeasureSpec.getMode(heightMeasureSpec)
var widthSize = MeasureSpec.getSize(widthMeasureSpec)
var heightSize = MeasureSpec.getSize(heightMeasureSpec)
//如果两个的模式同时为EXACTlY
if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
myHeight = heightSize
myWidth = widthSize
} else {
//如果有一个模式为 AT_MOST
myWidth = defaultValue
myHeight = defaultValue
}
setMeasuredDimension(myWidth, myHeight)
}
}
有一个为AT_MOST的时候
同时为EXACTLY得时候
-
自定义ViewGroup时,什么时候使用
child.measure
什么时候使用measureChild
- 使用
child.measure
当不想使用xml中给出的值的时候或者说子View或者子ViewGroup(统称子控件)的大小需要自己写出、控制的时候。比如我想自己设置
子控件宽为:childWidth --- 模式为:EXACLTLY,
子控件高为:childHeight --- 模式为:EXACTLY
var childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth,MeasureSpec.EXACTLY)
var childHeightSpec = MeasureSpec.makeMeasureSpec(childHeight,MeasureSpec.EXACTLY)
自己make子控件的measureSpec,然后调用child的measure方法设置子控件的实际大小
child.measure(childWidthSpec,childHeightSpec)
- 使用
measureChild
当xml文件中已经指定了子控件的大小,并且我就像按照这个大小模式来设置我的子控件的实际大小measureChild(child, widthMeasureSpec, heightMeasureSpec)
第一个参数是哪一个子控件,后面两个参数是父控件的宽高测量建议。
源码:
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
而其中的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 = 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);
}
- 调用
measureChild
方式实际上就是根据父容器的大小和模式以及自己的布局参数来确定子控件的measureSpec
- 而调用
child.meaure
实际上就是不想依据系统默认的方式来确定子控件的measureSpec,而是自己根据自己的实际需求来手动调用measure.makeMeasureSpec()
来确定子控件的measureSpec