View的measure过程由measure方法完成,改方法是一个final类型的方法,子类不能重写,在该方法中调用onMeasure方法。下面针对View和ViewGroup讨论
View的measure过程
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
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;
}
在onMeasure中通过getDefaultSize方法根据宽高的MeasureSpec来获得宽高的测量值。
分两种情况讨论:
1. 当getDefaultSize的参数widthMeasureSpec为AT_MOST或EXACTLY时测量值就是由MeasureSpce.getSize获得的大小,即specSize:表示为父容器剩余空间的大小;
注意:关于返回值specSize 时的讨论
- 由于获得的specSize表示为父容器剩余空间的大小,widthMeasureSpec为AT_MOST或EXACTLY时都返回此值。这与在布局中使用match_parent效果一致,但明显布局使用wrap_content时,view的尺寸并非如此
- 通过查看源码,确实是如此。拿textview的宽度来说,它对wrap_content的情况做的特殊处理就是测量内容(字符)的长度等等,再根据计算结果和传入的宽度值做比较取小,从而得到那个初始值。
- 所以直接继承View的自定义空间我们需要重写onMeasure方法设置wrap_content时的自身大小。方案是指定一个默认大小,没有固定依据,依情况而定,如TextView完成对内容的测量
2. 当参数为UNSPECIFIED时即返回由getSuggestedMinimumWidth方法获取的大小。(这种情况一般发生在系统内部,不用太关心)
两种情况:
- 如果view没有设置背景,那么View的宽度为mMinWidth,即android:minWidth属性指定的值,默认为0;
- 如果View有设置背景,那么宽度我Drawable的原始宽度,如果Drawable没有原始值则返回0;
ViewGroup的measure过程
前面分析了View的measure过程,那么ViewGroup是一个抽象类没有重写onMeasure。
通过measureChildren方法开始测量过程。
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);
}
ViewGroup在measure时,会通过measureChild对每个子View进行measure。
- measureChild中先取出子View的LayoutParams
- 然后getChildMeasureSpec创建子元素的MeasureSpec
- 接着将MeasureSpec传递给View的measure方法进行测量
网友评论