【转载】快速理解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的测量过程由更深刻的求知欲的,推荐读者能够本身看一下源码。

相关文章
相关标签/搜索