View可以看成一個樹形結構,父控件是父節點,子控件是子節點。View的繪制過程就是遍歷這棵樹。
View的繪制有三步:
- measure:測量View的Width和Height,
- layout:布局View(left,right,top,bottom),指定View和手機屏幕的上下左右的距離。
- draw:繪圖
以上的步驟必須按照順序來。(順便說一下,以上三個步驟發生在View的構造方法之後。)
一、measure
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中描述的那樣,但是子視圖的具體大小取決於多方面的。