View和ViewGroup 的measure过程
视图有3大流程,measure,layout,draw,即测量,布局和绘制.我们先分析第一个流程measure.
1.View 的measure过程
View 的measure过程是由其measure()来完成,这个方法是final类型的,也就是不能被之类重写,同时measure()会转调onMeasure(),所以我们暂时重点关注onMeasure()就可以了.实际我们在自定义一个视图控件的时候也是重写onMeasure方法.
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
上面的方法就是调用了setMeasuredDismension()来设置mMeasuredWidth和mMeasuredHeight,这2个变量会在View 的layout 流程和draw流程用到.但是在调用
setMeasuredDismension 时它的参数是由
getDefaultSize
()来得到的.具体源码如下:
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
}
其中getDefaultSize就是根据View 的MeasureSpec和另一个参数size来获取返回值.
其中我们需要关注的是
MeasureSpec.AT_MOST和MeasureSpec.EXACTLY2种模式,也就是都返回specSize.这个specSize也就是由由父容器的MeasureSpec和View自身的LayoutParams共同生成的View的MeasureSpec调用getsize得到的大小,可以理解为View 的测量之后得到的宽高大小.
上面另外一种模式
MeasureSpec
.
UNSPECIFIED
,是用来系统内部测量的.这种情况下返回值是size,也就是的
getSuggestedMinimumWidth和
getSuggestedMinimumHeight
返回值大小.
对
getSuggestedMinimumWidth()源码
可以得出结论:如果View没有设置背景,View 的宽度就是android:minWidth的值,如果没有设置这个值就是0,如果设置了背景,就是
android:minWidth和背景图标的宽度的较大值.
2.ViewGroup 的measure过程
由于
measure不能被重写,而且ViewGroup没有重写onMeasure(),所以是通过其他方法来处理这个measure流程.实际是使用的measureChildren().
ViewGroup.java
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
final int size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; ++i) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {//是否可见
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);
}
上面的代码就是循环调用child view 的measure()方法,如果child view 也有child,那么就会递归执行这个measureChildren().
其中getChildMeasureSpec
()之前已经笔记"View MeasureSpec 和LayoutParams关系
"里面分析过,这里就重复分析.
所以我们可以知道ViewGroup没有执行具体的测量过程,只是调用child view 的measure()方法.这是因为ViewGroup子类太多,不好统一处理.实际是各个子类去重写onMeasure来自己处理的.