【轉載】快速理解android View的測量onMeasure()與MeasureSpec

筆者以前有一篇文章已經使用onMeasure()解決了listview與scollview的顯示衝突問題,博客地址以下:html

onMeasure簡單方法 完美解決ListView與ScollView衝突問題!java

 

在此就針對View的測量以及onMeasure()涉及的幾個問題作一個詳細解釋:android

1、MeasureSpec的概念:佈局

MeasureSpec經過將SpecMode和SpecSize打包成一個int值來避免過多的對象內存分配,爲了方便操做,其提供了打包和解包的方法。SpecMode和SpecSize也是一個int值,一組SpecMode和SpecSize能夠打包爲一個MeasureSpec,而一個MeasureSpec能夠經過解包的形式來得出其原始的SpecMode和SpecSize。post

讀者只要記住如下一句話便可:spa

MeasureSpec的值由specSize和specMode共同組成的,其中specSize記錄的是大小,specMode記錄的是規格。.net

 

2、SpecMode的三種模式:htm

 

1. EXACTLY對象

當咱們將控件的「layout_width」屬性或者「layout_height」屬性指定爲具體數值時,好比「android:layout_width="200dp"」,或者指定爲「match_parent」時,系統會使用這個模式。blog

2. AT_MOST

當控件的「layout_width」屬性或者「layout_height」屬性設置爲「wrap_content」時,控件大小通常會隨着內容的大小而變化,可是不管多大,也不能超過父控件的尺寸。

3. UNSPECIFIED

表示開發人員能夠將視圖按照本身的意願設置成任意的大小,沒有任何限制。這種狀況比較少見,通常在繪製自定義View的時候纔會用到。

 

3、View的測量到底和什麼有關呢?

要探其原理,首先要和你們說明一點,一個View只須要MeasureSpec肯定,那麼在onMeasure中就能夠測量它的寬高,因此咱們能夠將問題直接轉化成「一個View的MeasureSpec是如何肯定的呢?」

 

普通的View的measure過程由VIewGroup傳遞而來,此處咱們根據源碼來作一個解釋,先看一下ViewGroup中的measureChildWithMargins():

 

[java]  view plain  copy
 
  1. protected void measureChildWithMargins(View child,  
  2.             int parentWidthMeasureSpec, int widthUsed,  
  3.             int parentHeightMeasureSpec, int heightUsed) {  
  4.         final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();  
  5.   
  6.         final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,  
  7.                 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin  
  8.                         + widthUsed, lp.width);  
  9.         final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,  
  10.                 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin  
  11.                         + heightUsed, lp.height);  
  12.   
  13.         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);  
  14.     }  
從中,咱們能夠看到一個view的寬高,都是經過getChildMeasureSpec()這個方法得到的,那麼這裏面又是怎麼實現的呢?咱們不妨Control+左鍵點進去看一下,代碼以下:

 

 

[java]  view plain  copy
 
  1. public static int getChildMeasureSpec(int spec, int padding, int childDimension) {  
  2.         int specMode = MeasureSpec.getMode(spec);  
  3.         int specSize = MeasureSpec.getSize(spec);  
  4.   
  5.         int size = Math.max(0, specSize - padding);  
  6.   
  7.         int resultSize = 0;  
  8.         int resultMode = 0;  
  9.   
  10.         switch (specMode) {  
  11.         // Parent has imposed an exact size on us  
  12.         case MeasureSpec.EXACTLY:  
  13.             if (childDimension >= 0) {  
  14.                 resultSize = childDimension;  
  15.                 resultMode = MeasureSpec.EXACTLY;  
  16.             } else if (childDimension == LayoutParams.MATCH_PARENT) {  
  17.                 // Child wants to be our size. So be it.  
  18.                 resultSize = size;  
  19.                 resultMode = MeasureSpec.EXACTLY;  
  20.             } else if (childDimension == LayoutParams.WRAP_CONTENT) {  
  21.                 // Child wants to determine its own size. It can't be  
  22.                 // bigger than us.  
  23.                 resultSize = size;  
  24.                 resultMode = MeasureSpec.AT_MOST;  
  25.             }  
  26.             break;  
  27.   
  28.         // Parent has imposed a maximum size on us  
  29.         case MeasureSpec.AT_MOST:  
  30.             if (childDimension >= 0) {  
  31.                 // Child wants a specific size... so be it  
  32.                 resultSize = childDimension;  
  33.                 resultMode = MeasureSpec.EXACTLY;  
  34.             } else if (childDimension == LayoutParams.MATCH_PARENT) {  
  35.                 // Child wants to be our size, but our size is not fixed.  
  36.                 // Constrain child to not be bigger than us.  
  37.                 resultSize = size;  
  38.                 resultMode = MeasureSpec.AT_MOST;  
  39.             } else if (childDimension == LayoutParams.WRAP_CONTENT) {  
  40.                 // Child wants to determine its own size. It can't be  
  41.                 // bigger than us.  
  42.                 resultSize = size;  
  43.                 resultMode = MeasureSpec.AT_MOST;  
  44.             }  
  45.             break;  
  46.   
  47.         // Parent asked to see how big we want to be  
  48.         case MeasureSpec.UNSPECIFIED:  
  49.             if (childDimension >= 0) {  
  50.                 // Child wants a specific size... let him have it  
  51.                 resultSize = childDimension;  
  52.                 resultMode = MeasureSpec.EXACTLY;  
  53.             } else if (childDimension == LayoutParams.MATCH_PARENT) {  
  54.                 // Child wants to be our size... find out how big it should  
  55.                 // be  
  56.                 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;  
  57.                 resultMode = MeasureSpec.UNSPECIFIED;  
  58.             } else if (childDimension == LayoutParams.WRAP_CONTENT) {  
  59.                 // Child wants to determine its own size.... find out how  
  60.                 // big it should be  
  61.                 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;  
  62.                 resultMode = MeasureSpec.UNSPECIFIED;  
  63.             }  
  64.             break;  
  65.         }  
  66.         return MeasureSpec.makeMeasureSpec(resultSize, resultMode);  
  67.     }  

 

代碼比較長,讀者能夠比較焦急——不用懼怕,咱們不須要徹底理解它的原理,咱們只須要知道View的測量是如何實現的就好了。

看到源碼方法中的三個參數,而且比較measureChildWithMargins()方法中傳遞給getChildMeasureSpec()三個值,咱們很快就能夠理解,一個View的測量過程是由父佈局的MeasureSpec和該View的LayoutParams決定的。

 

讀者能夠看measureChildWithMargins()中以下代碼:

 

[java]  view plain  copy
 
  1. final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,  
  2.                 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin  
  3.                         + widthUsed, lp.width);  

 

 

要測量子部局的寬度的MeasureSpec,須要傳入3個參數:

第一個參數:父佈局的寬度的MeasureSpec

第二個參數:子部局的padding值,子部局的LayoutParams的Margin值

第三個參數:子部局的LayoutParams的寬度

 

 

 

若是對View的測量過程由更深刻的求知慾的,推薦讀者能夠本身看一下源碼。

相關文章
相關標籤/搜索