MeasureSpec总结
MeasureSpec
View的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec,然后根据这个MeasureSpec来测量出View的宽高。MeasureSpec代表一个32位int值,高2位代表SpecMode(测量模式),低30位代表SpecSize(在某个测量模式下的规格大小)
//获取SpecSize:
int specSize = MeasureSpec.getSize(measureSpec)
//获取specMode
int measureSpec=MeasureSpec.makeMeasureSpec(size, mode);
SpecMode一共有三种:
-
UNSPECIFIED :父容器不对View进行任何限制,要多大给多大,一般用于系统内部
-
EXACTLY:父容器检测到View所需要的精确大小,这时候View的最终大小就是SpecSize所指定的值,对应LayoutParams中的match_parent和具体数值这两种模式
-
AT_MOST :对应View的默认大小,不同View实现不同,View的大小不能大于父容器的SpecSize,对应LayoutParams中的wrap_content
MeasureSpec和LayoutParams的对应关系
View的MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同决定。
对于普通View来说,View的measure过程由ViewGroup传递过来,measureChildWithMargins源码
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
measureChildWithMargins()方法对子元素进行measure,在调用子元素的measure方法之前先通过
measureChildWithMargins()方法得到子元素的MeasureSpec,子元素的MeasureSpec与父容器的MeasureSpec和自身的LayoutParams有关,还与view的margin和padding有关。
View 的MeasureSpec创建规则(getChildMeasureSpec方法的表格呈现)
- 当View采用固定宽/高时(即设置固定的dp/px),不管父容器的MeasureSpec是什么,View的MeasureSpec都是EXACTLY模式,并且大小遵循我们设置的值。
- 当View的宽/高是match_parents时,如果父容器的模式是精准模式,那么View也是精准模式并且其大小是父容器的剩余空间;如果父容器是最大模式那么View也是最大模式并且其大小不会超过父容器的剩余空间
- 当View的宽/高是wrap_content时,View的MeasureSpec都是AT_MOST模式并且其大小不能超过父容器的剩余空间。
只要提供父容器的MeasureSpec和子元素的LayoutParams,就可以确定出子元素的MeasureSpec,进一步便可以确定出测量后的大小。