转载快速理解android View的测量onMeasure()与MeasureSpec

金鞍玉勒寻芳客,未信我庐别有春。这篇文章主要讲述转载快速理解android View的测量onMeasure()与MeasureSpec相关的知识,希望能为你提供帮助。
笔者之前有一篇文章已经使用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的宽度
 
 
 
【转载快速理解android View的测量onMeasure()与MeasureSpec】如果对View的测量过程由更深入的求知欲的,推荐读者可以自己看一下源码。
版权声明:本文为博主许佳佳原创文章,转载请务必注明出处。 https://blog.csdn.net/Double2hao/article/details/51553703

    推荐阅读