View可以看成一个树形结构,父控件是父节点,子控件是子节点。View的绘制过程就是遍历这棵树。
View的绘制有三步:
- measure:测量View的Width和Height,
- layout:布局View(left,right,top,bottom),指定View和手机屏幕的上下左右的距离。
- draw:绘图
以上的步骤必须按照顺序来。(顺便说一下,以上三个步骤发生在View的构造方法之后。)
measure是绘制视图的第一步,因为只有知道的View的大小(Width和Height)才能绘图。
我们在编写layout的xml文件的时候,会遇到layout_width和layout_height两个属性,对于这两个属性我们有三个选择:fill_parent、wrap_content和具体值,measure就是用来处理fill_parent、wrap_content两个属性的,在绘图的时候,要知道具体的值,所以要计算fill_parent、wrap_content的具体值。
下面是几个重要的函数和参数:
- public final void measure(int widthMeasureSpec, int heightMeasureSpec)
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
- protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec)
- protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)
- protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed)
前两个方法是View类里面的方法,后三个方法是ViewGroup类里面的方法。
先来看看measure的源码:
/**
* <p>
* This is called to find out how big a view should be. The parent
* supplies constraint information in the width and height parameters.
* </p>
*
* <p>
* The actual mesurement work of a view is performed in
* {@link #onMeasure(int, int)}, called by this method. Therefore, only
* {@link #onMeasure(int, int)} can and must be overriden by subclasses.
* </p>
*
*
* @param widthMeasureSpec Horizontal space requirements as imposed by the
* parent
* @param heightMeasureSpec Vertical space requirements as imposed by the
* parent
*
* @see #onMeasure(int, int)
*/
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||
heightMeasureSpec != mOldHeightMeasureSpec) {
// first clears the measured dimension flag
mPrivateFlags &= ~MEASURED_DIMENSION_SET;
if (ViewDebug.TRACE_HIERARCHY) {
ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
}
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
// flag not set, setMeasuredDimension() was not invoked, we raise
// an exception to warn the developer
if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
throw new IllegalStateException("onMeasure() did not set the"
+ " measured dimension by calling"
+ " setMeasuredDimension()");
}
mPrivateFlags |= LAYOUT_REQUIRED;
}
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
}
measure方法是final类型的,所以不能被继承,不能被复写。而measure方法里面调用了onMeasure方法,onMeasure方法可以被继承,可以被复写。下面是onMeasure源码:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
onMeasure默认的实现仅仅调用了setMeasuredDimension,setMeasuredDimension函数是一个很关键的函数,它对View的成员变量mMeasuredWidth和mMeasuredHeight变量赋值,而measure的主要目的就是对View树中的每个View的mMeasuredWidth和mMeasuredHeight进行赋值,一旦这两个变量被赋值,则意味着该View的测量工作结束。
setMeasuredDimension源码如下:
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= MEASURED_DIMENSION_SET;
}
再来看下MeasureSpec这个类,MeasureSpec参数的值为int型,分为高32位和低16为,高32位保存的是specMode,低16位表示specSize,specMode分三种:
- MeasureSpec.UNSPECIFIED:父视图不对子视图施加任何限制,子视图可以得到任意想要的大小
- MeasureSpec.EXACTLY:父视图希望子视图的大小是specSize中指定的大小
- MeasureSpec.AT_MOST:子视图的大小最多是specSize中的大小
以上施加的限制只是父视图“希望”子视图的大小按MeasureSpec中描述的那样,但是子视图的具体大小取决于多方面的