一、MeasureSpc類說明
SDK的介紹:MeasureSpc類封裝了父View傳遞給子View的布局(layout)要求。每個MeasureSpc實例代表寬度或者高度
它有三種模式:
①、UNSPECIFIED(未指定),父元素部隊自元素施加任何束縛,子元素可以得到任意想要的大小;
②、EXACTLY(完全),父元素決定自元素的確切大小,子元素將被限定在給定的邊界里而忽略它本身大小;
③、AT_MOST(至多),子元素至多達到指定大小的值。
常用的三個函數:
static int getMode(int measureSpec) : 根據提供的測量值(格式),提取模式(上述三個模式之一)
static int getSize(int measureSpec) : 根據提供的測量值(格式),提取大小值(這個大小也就是我們通常所說的大小)
static int makeMeasureSpec(int size,int mode) : 根據提供的大小值和模式,創建一個測量值(格式)
MeasureSpc類源碼分析 其為View.java類的內部類,路徑:frameworksbasecorejavaandroidviewView.java

1 public class View implements ... { 2 ... 3 public static class MeasureSpec { 4 private static final int MODE_SHIFT = 30; //移位位數為30 5 //int類型占32位,向右移位30位,該屬性表示掩碼值,用來與size和mode進行"&"運算,獲取對應值。 6 private static final int MODE_MASK = 0x3 << MODE_SHIFT; 7 //向右移位30位,其值為00 + (30位0) , 即 0x0000(16進制表示) 8 public static final int UNSPECIFIED = 0 << MODE_SHIFT; 9 //向右移位30位,其值為01 + (30位0) , 即0x1000(16進制表示) 10 public static final int EXACTLY = 1 << MODE_SHIFT; 11 //向右移位30位,其值為02 + (30位0) , 即0x2000(16進制表示) 12 public static final int AT_MOST = 2 << MODE_SHIFT; 13 14 //創建一個整形值,其高兩位代表mode類型,其余30位代表長或寬的實際值。可以是WRAP_CONTENT、MATCH_PARENT或具體大小exactly size 15 public static int makeMeasureSpec(int size, int mode) { 16 return size + mode; 17 } 18 //獲取模式 ,與運算 19 public static int getMode(int measureSpec) { 20 return (measureSpec & MODE_MASK); 21 } 22 //獲取長或寬的實際值 ,與運算 23 public static int getSize(int measureSpec) { 24 return (measureSpec & ~MODE_MASK); 25 } 26 27 } 28 ...
MeasureSpec類的處理思路是:
右移運算,使int 類型的高兩位表示模式的實際值,其余30位表示其余30位代表長或寬的實際值----可以是WRAP_CONTENT、MATCH_PARENT或具體大小exactly size。 通過掩碼MODE_MASK進行與運算 “&”,取得模式(mode)以及長或寬(value)的實際值。
MeasureSpec.makeMeasureSpec方法,實際上這個方法很簡單:

1 public static int makeMeasureSpec(int size, int mode) { 2 return size + mode; 3 }
其用法如下:

1 int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED); 2 int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED); 3 ssidtext.measure(w, h); 4 int width =ssidtext.getMeasuredWidth(); 5 int height =ssidtext.getMeasuredHeight();
二、measure過程詳解
UI框架開始繪制時,皆是從ViewRoot.java類開始繪制的:
ViewRoot類簡要說明: 任何顯示在設備中的窗口,例如:Activity、Dialog等,都包含一個ViewRoot實例,該類主要用來與遠端 WindowManagerService交互以及控制(開始/銷毀)繪制。
1、開始UI繪制 , 具體繪制方法則是:

//開始View繪制流程 private void performTraversals(){ ... //這兩個值我們在后面討論時,在回過頭來看看是怎么賦值的。現在只需要記住其值MeasureSpec.makeMeasureSpec()構建的。 int childWidthMeasureSpec; //其值由MeasureSpec類構建 , makeMeasureSpec int childHeightMeasureSpec;//其值由MeasureSpec類構建 , makeMeasureSpec // Ask host how big it wants to be host.measure(childWidthMeasureSpec, childHeightMeasureSpec); ... } ...
2、調用measure()方法去做一些前期准備 measure()方法原型定義在View.java類中,final修飾符修飾,其不能被重載:

1 public class View implements ... { 2 ... 3 /** 4 * This is called to find out how big a view should be. The parent 5 * supplies constraint information in the width and height parameters. 6 * 7 * @param widthMeasureSpec Horizontal space requirements as imposed by the 8 * parent 9 * @param heightMeasureSpec Vertical space requirements as imposed by the 10 * parent 11 * @see #onMeasure(int, int) 12 */ 13 public final void measure(int widthMeasureSpec, int heightMeasureSpec) { 14 //判斷是否為強制布局,即帶有“FORCE_LAYOUT”標記 以及 widthMeasureSpec或heightMeasureSpec發生了改變 15 if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT || 16 widthMeasureSpec != mOldWidthMeasureSpec || 17 heightMeasureSpec != mOldHeightMeasureSpec) { 18 // first clears the measured dimension flag 19 //清除MEASURED_DIMENSION_SET標記 ,該標記會在onMeasure()方法后被設置 20 mPrivateFlags &= ~MEASURED_DIMENSION_SET; 21 22 // measure ourselves, this should set the measured dimension flag back 23 // 1、 測量該View本身的大小 ; 2 、 設置MEASURED_DIMENSION_SET標記,否則接寫來會報異常。 24 onMeasure(widthMeasureSpec, heightMeasureSpec); 25 26 // flag not set, setMeasuredDimension() was not invoked, we raise 27 // an exception to warn the developer 28 if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) { 29 throw new IllegalStateException("onMeasure() did not set the" 30 + " measured dimension by calling" + " setMeasuredDimension()"); 31 } 32 33 mPrivateFlags |= LAYOUT_REQUIRED; //下一步是layout了,添加LAYOUT_REQUIRED標記 34 } 35 36 mOldWidthMeasureSpec = widthMeasureSpec; //保存值 37 mOldHeightMeasureSpec = heightMeasureSpec; //保存值 38 } 39 ...
參數widthMeasureSpec和heightMeasureSpec 由父View構建,表示父View給子View的測量要求。其值地構建如下:
measure()方法顯示判斷是否需要重新調用設置改View大小,即調用onMeasure()方法,然后操作兩個標識符:
①、重置MEASURED_DIMENSION_SET : onMeasure()方法中,需要添加該標識符,否則,會報異常;
②、添加LAYOUT_REQUIRED : 表示需要進行layout操作。最后,保存當前的widthMeasureSpec和heightMeasureSpec值。
3、調用onMeasure()方法去真正設置View的長寬值,其默認實現為:

1 //設置該View本身地大小 2 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 3 setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), 4 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); 5 } 6 7 8 //@param size參數一般表示設置了android:minHeight屬性或者該View背景圖片的大小值 9 public static int getDefaultSize(int size, int measureSpec) { 10 int result = size; 11 int specMode = MeasureSpec.getMode(measureSpec); 12 int specSize = MeasureSpec.getSize(measureSpec); 13 14 //根據不同的mode值,取得寬和高的實際值。 15 switch (specMode) { 16 case MeasureSpec.UNSPECIFIED: //表示該View的大小父視圖未定,設置為默認值 17 result = size; 18 break; 19 case MeasureSpec.AT_MOST: //表示該View的大小由父視圖指定了 20 case MeasureSpec.EXACTLY: 21 result = specSize; 22 break; 23 } 24 return result; 25 } 26 27 //獲得設置了android:minHeight屬性或者該View背景圖片的大小值, 最為該View的參考值 28 protected int getSuggestedMinimumWidth() { 29 int suggestedMinWidth = mMinWidth; // android:minHeight 30 if (mBGDrawable != null) { // 背景圖片對應地Width。 31 final int bgMinWidth = mBGDrawable.getMinimumWidth(); 32 if (suggestedMinWidth < bgMinWidth) { 33 suggestedMinWidth = bgMinWidth; 34 } 35 } 36 37 return suggestedMinWidth; 38 } 39 //設置View在measure過程中寬和高 40 protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { 41 mMeasuredWidth = measuredWidth; 42 mMeasuredHeight = measuredHeight; 43 mPrivateFlags |= MEASURED_DIMENSION_SET; //設置了MEASURED_DIMENSION_SET標記 44 }
主要功能就是根據該View屬性(android:minWidth和背景圖片大小)和父View對該子View的"測量要求",設置該View的 mMeasuredWidth 和 mMeasuredHeight 值。
這兒只是一般的View類型地實現方法。一般來說,父View,也就是ViewGroup類型,都需要在重寫onMeasure()方法,遍歷所有子View,設置每個子View的大小。基本思想如下:遍歷所有子View,設置每個子View的大小。偽代碼表示為:

1 //某個ViewGroup類型的視圖 2 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 3 //必須調用super.ononMeasure()或者直接調用setMeasuredDimension()方法設置該View大小,否則會報異常。 4 super.onMeasure(widthMeasureSpec , heightMeasureSpec) 5 6 //遍歷每個子View 7 for(int i = 0 ; i < getChildCount() ; i++){ 8 View child = getChildAt(i); 9 //調用子View的onMeasure,設置他們的大小。childWidthMeasureSpec , childHeightMeasureSpec ? 10 child.onMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 11 } 12 }
如何去設置每個子View的大小,基本思想也如同我們之前描述的思想:遍歷所有子View,設置每個子View的大小。

1 //widthMeasureSpec 和 heightMeasureSpec 表示該父View的布局要求 2 //遍歷每個子View,然后調用measureChild()方法去實現每個子View大小 3 protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) { 4 final int size = mChildrenCount; 5 final View[] children = mChildren; 6 for (int i = 0; i < size; ++i) { 7 final View child = children[i]; 8 if ((child.mViewFlags & VISIBILITY_MASK) != GONE) { // 不處於 “GONE” 狀態 9 measureChild(child, widthMeasureSpec, heightMeasureSpec); 10 } 11 } 12 } 13 //測量每個子View高寬時,清楚了該View本身的邊距大小,即android:padding屬性 或android:paddingLeft等屬性標記 14 protected void measureChild(View child, int parentWidthMeasureSpec, 15 int parentHeightMeasureSpec) { 16 final LayoutParams lp = child.getLayoutParams(); // LayoutParams屬性 17 //設置子View的childWidthMeasureSpec屬性,去除了該父View的邊距值 mPaddingLeft + mPaddingRight 18 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 19 mPaddingLeft + mPaddingRight, lp.width); 20 //設置子View的childHeightMeasureSpec屬性,去除了該父View的邊距值 mPaddingTop + mPaddingBottom 21 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 22 mPaddingTop + mPaddingBottom, lp.height); 23 child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
measureChildren()方法:遍歷所有子View,調用measureChild()方法去設置該子View的屬性值。 measureChild() 方法 : 獲取特定子View的widthMeasureSpec、heightMeasureSpec,調用measure()方法設置子View的實際寬高值。
getChildMeasureSpec()就是獲取子View的widthMeasureSpec、heightMeasureSpec值。

1 // spec參數 表示該父View本身所占的widthMeasureSpec 或 heightMeasureSpec值 2 // padding參數 表示該父View的邊距大小,見於android:padding屬性 或android:paddingLeft等屬性標記 3 // childDimension參數 表示該子View內部LayoutParams屬性的值,可以是wrap_content、match_parent、一個精確指(an exactly size), 4 // 例如:由android:width指定等。 5 public static int getChildMeasureSpec(int spec, int padding, int childDimension) { 6 int specMode = MeasureSpec.getMode(spec); //獲得父View的mode 7 int specSize = MeasureSpec.getSize(spec); //獲得父View的實際值 8 int size = Math.max(0, specSize - padding); //父View為子View設定的大小,減去邊距值, 9 10 int resultSize = 0; //子View對應地 size 實際值 ,由下面的邏輯條件賦值 11 int resultMode = 0; //子View對應地 mode 值 , 由下面的邏輯條件賦值 12 13 switch (specMode) { 14 // Parent has imposed an exact size on us 15 //1、父View是EXACTLY的 ! 16 case MeasureSpec.EXACTLY: 17 //1.1、子View的width或height是個精確值 (an exactly size) 18 if (childDimension >= 0) { 19 resultSize = childDimension; //size為精確值 20 resultMode = MeasureSpec.EXACTLY; //mode為 EXACTLY 。 21 } 22 //1.2、子View的width或height為 MATCH_PARENT/FILL_PARENT 23 else if (childDimension == LayoutParams.MATCH_PARENT) { 24 // Child wants to be our size. So be it. 25 resultSize = size; //size為父視圖大小 26 resultMode = MeasureSpec.EXACTLY; //mode為 EXACTLY 。 27 } 28 //1.3、子View的width或height為 WRAP_CONTENT 29 else if (childDimension == LayoutParams.WRAP_CONTENT) { 30 // Child wants to determine its own size. It can't be 31 // bigger than us. 32 resultSize = size; //size為父視圖大小 33 resultMode = MeasureSpec.AT_MOST; //mode為AT_MOST 。 34 } 35 break; 36 37 // Parent has imposed a maximum size on us 38 //2、父View是AT_MOST的 ! 39 case MeasureSpec.AT_MOST: 40 //2.1、子View的width或height是個精確值 (an exactly size) 41 if (childDimension >= 0) { 42 // Child wants a specific size... so be it 43 resultSize = childDimension; //size為精確值 44 resultMode = MeasureSpec.EXACTLY; //mode為 EXACTLY 。 45 } 46 //2.2、子View的width或height為 MATCH_PARENT/FILL_PARENT 47 else if (childDimension == LayoutParams.MATCH_PARENT) { 48 // Child wants to be our size, but our size is not fixed. 49 // Constrain child to not be bigger than us. 50 resultSize = size; //size為父視圖大小 51 resultMode = MeasureSpec.AT_MOST; //mode為AT_MOST 52 } 53 //2.3、子View的width或height為 WRAP_CONTENT 54 else if (childDimension == LayoutParams.WRAP_CONTENT) { 55 // Child wants to determine its own size. It can't be 56 // bigger than us. 57 resultSize = size; //size為父視圖大小 58 resultMode = MeasureSpec.AT_MOST; //mode為AT_MOST 59 } 60 break; 61 62 // Parent asked to see how big we want to be 63 //3、父View是UNSPECIFIED的 ! 64 case MeasureSpec.UNSPECIFIED: 65 //3.1、子View的width或height是個精確值 (an exactly size) 66 if (childDimension >= 0) { 67 // Child wants a specific size... let him have it 68 resultSize = childDimension; //size為精確值 69 resultMode = MeasureSpec.EXACTLY; //mode為 EXACTLY 70 } 71 //3.2、子View的width或height為 MATCH_PARENT/FILL_PARENT 72 else if (childDimension == LayoutParams.MATCH_PARENT) { 73 // Child wants to be our size... find out how big it should 74 // be 75 resultSize = 0; //size為0! ,其值未定 76 resultMode = MeasureSpec.UNSPECIFIED; //mode為 UNSPECIFIED 77 } 78 //3.3、子View的width或height為 WRAP_CONTENT 79 else if (childDimension == LayoutParams.WRAP_CONTENT) { 80 // Child wants to determine its own size.... find out how 81 // big it should be 82 resultSize = 0; //size為0! ,其值未定 83 resultMode = MeasureSpec.UNSPECIFIED; //mode為 UNSPECIFIED 84 } 85 break; 86 } 87 //根據上面邏輯條件獲取的mode和size構建MeasureSpec對象。 88 return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
每個View大小的設定都事由其父View以及該View共同決定的。但這只是一個期望的大小,每個View在測量時最終大小的設定是由setMeasuredDimension()最終決定的。因此,最終確定一個View的“測量長寬“是由以下幾個方面影響:
1、父View的MeasureSpec屬性;
2、子View的LayoutParams屬性 ;
3、setMeasuredDimension()或者其它類似設定 mMeasuredWidth 和 mMeasuredHeight 值的方法。

1 //設置View在measure過程中寬和高 2 protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { 3 mMeasuredWidth = measuredWidth; 4 mMeasuredHeight = measuredHeight; 5 mPrivateFlags |= MEASURED_DIMENSION_SET; //設置了MEASURED_DIMENSION_SET標記 6 }