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

來源:互聯網
上載者:User

標籤:過多   mos   需要   開發人員   word   span   nts   maximum   著作權   

筆者之前有一篇文章已經使用onMeasure()解決了listview與scollview的顯示衝突問題,部落格地址如下:

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

 

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

一、MeasureSpec的概念:

MeasureSpec通過將SpecMode和SpecSize打包成一個int值來避免過多的對象記憶體配置,為了方便操作,其提供了打包和解包的方法。SpecMode和SpecSize也是一個int值,一組SpecMode和SpecSize可以打包為一個MeasureSpec,而一個MeasureSpec可以通過解包的形式來得出其原始的SpecMode和SpecSize。

讀者只要記住以下一句話即可:

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

 

二、SpecMode的三種模式:

 

1. EXACTLY

當我們將控制項的“layout_width”屬性或者“layout_height”屬性指定為具體數值時,比如“android:layout_width="200dp"”,或者指定為“match_parent”時,系統會使用這個模式。

2. AT_MOST

當控制項的“layout_width”屬性或者“layout_height”屬性設定為“wrap_content”時,控制項大小一般會隨著內容的大小而變化,但是無論多大,也不能超過父控制項的尺寸。

3. UNSPECIFIED

表示開發人員可以將視圖按照自己的意願設定成任意的大小,沒有任何限制。這種情況比較少見,一般在繪製自訂View的時候才會用到。

 

三、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的測量過程由更深入的求知慾的,推薦讀者可以自己看一下源碼。

著作權聲明:本文為博主許佳佳原創文章,轉載請務必註明出處。 51553703

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.