自己的一點理解:
其實measureSpec就是view的一個內(nèi)部類似踱,封裝了這個view對象的Mode和Size分别,封裝在一個Int類型中捶码,4字節(jié)*8位=32位,前2位封裝的mode豁鲤,后30位封裝的size
推薦的兩篇博客地址:
先說下結(jié)論秽誊,onMeasure是測量view或viewGroup時系統(tǒng)調(diào)用的,它的參數(shù)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(統(tǒng)稱子控件)的大小需要自己寫出最易、控制的時候。比如我想自己設(shè)置
子控件寬為:childWidth --- 模式為:EXACLTLY炫狱,
子控件高為:childHeight --- 模式為:EXACTLY
var childWidthSpec = MeasureSpec.makeMeasureSpec(childWidth,MeasureSpec.EXACTLY)
var childHeightSpec = MeasureSpec.makeMeasureSpec(childHeight,MeasureSpec.EXACTLY)
自己make子控件的measureSpec藻懒,然后調(diào)用child的measure方法設(shè)置子控件的實際大小
child.measure(childWidthSpec,childHeightSpec)
- 使用
measureChild
當xml文件中已經(jīng)指定了子控件的大小,并且我就像按照這個大小模式來設(shè)置我的子控件的實際大小measureChild(child, widthMeasureSpec, heightMeasureSpec)
第一個參數(shù)是哪一個子控件视译,后面兩個參數(shù)是父控件的寬高測量建議嬉荆。
源碼:
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);
}
- 調(diào)用
measureChild
方式實際上就是根據(jù)父容器的大小和模式以及自己的布局參數(shù)來確定子控件的measureSpec
- 而調(diào)用
child.meaure
實際上就是不想依據(jù)系統(tǒng)默認的方式來確定子控件的measureSpec,而是自己根據(jù)自己的實際需求來手動調(diào)用measure.makeMeasureSpec()
來確定子控件的measureSpec