MeasureSpec的简单说明

   注:Spec为specification的缩写,以为规格或者说明书的意思(英语不好,专门 用英语翻译软件翻译了一下)。所以顾名思义该类的所以就是定义View的测量规格或者测量规则。这个类是View里面的嵌套内部类,提供了三个对外公开的static变量UNSPECIFIED,EXACTLY,AT_MOST,,这三个变量统称为specMode,对于一个View来说它的宽和高各有属于自己的specMode,至于其具体作用后面会有说说明。MeasureSpec提供了三个方法

    1)makeMeasureSpec(int size,int mode):size参数由程序员自己设定,mode必须是specMode的三个值中的一个

    2)getMode(int measureSpec):见名知意,方法返回specMode的三个值中的一个,注意方法参数measureSpec,这个参数的值是怎么得来的呢?正是由makeMeasureSpec方法计算出来的

   3)getSize(int measureSpec):获取View的大小,方法参数的值同样是由makeMeasureSpec计算的出来的。

     注意:看android源码的时候有一个小技巧:凡是参数的名字中带有spec后缀的参数,该值都是由makeMeasureSpec来计算出来的,这也是代码中良好命名习惯带来的好处之一。

    android的绘制流程中,第一个流程是Measure测量流程,而MeasureSpec在测量的过程中起到了至关重要的作用,不论是自定义View还是android自带的View,只要重写了onMearsure方法都能发现MeasureSpec的影子。测量过程中View类提供了measure方法,在该方法中调用了onMeasure方法,先看看这两份方法的签名 measure(int widthMeasureSpec, int heightMeasureSpec)、onMeasure(int widthMeasureSpec, int heightMeasureSpec),onMeasure方法中的参数是由measure方法直接传过去的;但是measure方法的参数是由谁来传过来的呢?还是那句话,看看方法名的后缀都有spec,这肯定和MeasureSpec方法有关了,确定得说他们的值就是有makeMeasureSpec方法来决定的。事实上正是如此:在measure的时候measure方法是由ViewRoot的performTraversals来调用的,在该方法中调用了getRootMeasureSpec方法,详见这位大神的博客

  上面一直强调参数命名参数后缀为spec的方法跟MeasureSpec的关系密切,下面就顺着这个思路沿着View的继承机构挑几个类来查看分析MeasureSpec具体作用的体现。View类就有onMeasure和Measure方法,上面已经做了简单的说明,下面具体看看ViewGroup的方法,在viewGroup里面是没有重写onMeasure方法的,换句话说方法参数带有spec后缀的只有measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec),measureChildren(int widthMeasureSpec, int heightMeasureSpec)和measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed)。在这里挑measureChild方法来进行说明:

 
  1. protected void measureChild(View child, int parentWidthMeasureSpec,

  2. int parentHeightMeasureSpec) {

  3. final LayoutParams lp = child.getLayoutParams();

  4.  
  5. final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,

  6. mPaddingLeft + mPaddingRight, lp.width);

  7. final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,

  8. mPaddingTop + mPaddingBottom, lp.height);

  9. //执行child的measure流程

  10. child.measure(childWidthMeasureSpec, childHeightMeasureSpec);

  11. }

对于以上代码要注意点就是方法参数parentWidthMeasureSpec和parentHeightMeasureSpec说明是父View的Spec,并且在代码里调用了getChildMeasureSpec方法。下面就看看getChildMeasureSpec的方法具体都干了些什么好事。

   方法参数说明:padding:当前父view的内边距
        childDimension: 子视图想要绘制的准确大小,其值可以是LayoutParams对象的width或者height变量。也就ch是xml文件中layout_height或者layout_width的值,可以是
         match_parent和wrap_content,也可以是具体的值

         spec:可以是父View经过makeMeasureSpec计算的值,也可以传递用户自己通过makeMeasureSpec计算的值(如果在你自己的代码中调用getChildMeasureSpec的话)

 
  1. public static int getChildMeasureSpec(int spec, int padding, int childDimension) {

  2. //1.获取specMode,注意此时是父类的spec mode

  3. int specMode = MeasureSpec.getMode(spec);

  4. //获取view的size

  5. int specSize = MeasureSpec.getSize(spec);

  6.  
  7. //设置父view的size

  8. int size = Math.max(0, specSize - padding);

  9.  
  10. //因为spec有size和mode共同构成,所以定义了如下两个变量,经过处理后供makeMeasureSpec使用

  11. int resultSize = 0;

  12. int resultMode = 0;

  13.  
  14. //解析specMode并做相应的处理

  15. switch (specMode) {

  16. // Parent has imposed an exact size on us

  17. //当父View的spec mode为 EXACTLY 时,父View强加给子View一个确切的大小

  18. case MeasureSpec.EXACTLY:

  19. //当用户设置了宽度值,比如xml文件里面设置了dimen值,直接用之

  20. if (childDimension >= 0) {

  21. resultSize = childDimension;

  22. //在子view自己设置了size的情况下,比如xml文件里面layout_width或者layout_height设置了具体的dimen值

  23. resultMode = MeasureSpec.EXACTLY;

  24. } else if (childDimension == LayoutParams.MATCH_PARENT) {//如果子View layout_width或者layout_height为mactch_parent

  25. // Child wants to be our size. So be it.

  26. //子view的大小与父view的大小一样

  27. resultSize = size;

  28. //此时赋值为EXACTLY

  29. resultMode = MeasureSpec.EXACTLY;

  30. } else if (childDimension == LayoutParams.WRAP_CONTENT) {//如果子View layout_width或者layout_height为wrap_content

  31. // Child wants to determine its own size. It can't be

  32. // bigger than us.

  33. //子view的大小由自己来决定,但是不能超过父View的大小

  34. resultSize = size;

  35. //此时赋值为At_MOST

  36. resultMode = MeasureSpec.AT_MOST;

  37. }

  38. break;

  39.  
  40. // Parent has imposed a maximum size on us

  41. //当父View的spec mode为 AT_MOST 时,父View强加给一个最大的size给子view

  42. case MeasureSpec.AT_MOST:

  43. //当用户设置了宽度值,比如xml文件里面设置了dimen值,直接用之

  44. if (childDimension >= 0) {

  45. // Child wants a specific size... so be it

  46. resultSize = childDimension;

  47. //在子view自己设置了size的情况下,比如xml文件里面layout_width或者layout_height设置了具体的dimen值

  48. resultMode = MeasureSpec.EXACTLY;

  49. } else if (childDimension == LayoutParams.MATCH_PARENT) {//如果子View layout_width或者layout_height为mactch_parent

  50. // Child wants to be our size, but our size is not fixed.

  51. // Constrain child to not be bigger than us.

  52. //此时父view的大小和子view的大小一样

  53. resultSize = size;

  54. //此时mode 为AT_MOST

  55. resultMode = MeasureSpec.AT_MOST;

  56. } else if (childDimension == LayoutParams.WRAP_CONTENT) {//如果子View layout_width或者layout_height为wrap_content

  57. // Child wants to determine its own size. It can't be

  58. // bigger than us.

  59. //子view的大小由自己来决定,但是不能超过父View的大小

  60. resultSize = size;

  61. //此时mode 为AT_MOST

  62. resultMode = MeasureSpec.AT_MOST;

  63. }

  64. break;

  65.  
  66. // Parent asked to see how big we want to be

  67. //在父View的specMode为UNSPECIFIED,让子view决定自己有多大

  68. case MeasureSpec.UNSPECIFIED:

  69. //当用户设置了宽度值,比如xml文件里面设置了dimen值,直接用之

  70. if (childDimension >= 0) {

  71. // Child wants a specific size... let him have it

  72. resultSize = childDimension;

  73. //在子view自己设置了size的情况下,比如xml文件里面layout_width或者layout_height设置了具体的dimen值

  74. resultMode = MeasureSpec.EXACTLY;

  75. } else if (childDimension == LayoutParams.MATCH_PARENT) {

  76. // Child wants to be our size... find out how big it should

  77. // be

  78. resultSize = 0;

  79. resultMode = MeasureSpec.UNSPECIFIED;

  80. } else if (childDimension == LayoutParams.WRAP_CONTENT) {

  81. // Child wants to determine its own size.... find out how

  82. // big it should be

  83. resultSize = 0;

  84. resultMode = MeasureSpec.UNSPECIFIED;

  85. }

  86. break;

  87. }

  88.  
  89. //最后调用makeMeasureSpec方法来提供子view的spec

  90. return MeasureSpec.makeMeasureSpec(resultSize, resultMode);

  91. }

通过分析这个方法可以得出如下结论:从这个方法里可以看出父view的measureSpec在某种程度上决定了子view的measureSpec

1)不论父View 的specMode是EXACTLY、UNSPECIFIED、AT_MOST的哪一种,如果子view在xml文件里面把layout_width或者layout_height这是了具体
  的dimen值、或者在代码里调用相应的方法为view的宽度或者高度设置了具体的值,那么此时widthSpec的mode 或者heightSpec的mode就是EXACLTY,size就等于layout_width或者
  layout_height的值。此时子view的specMode值不受父View的specMode的影响。
2)当父View的specMode为EXACTLY的时候:父View强加给子View一个确切的大小,有如下两种情况
    2.1)子View的layout_width或者layout_height设置为MATCH_PARENT的时候,子View的specMode为EXACTLY
    2.2)子View的layout_width或者layout_height设置为WRAP_CONTENT的时候,子View的specMode为AT_MOST
    2.3)不论子view为match_parent或者wrap_content,resultSize都等于父类的size.
3)当父view的specMode为AT_MOST的时候:父View强加给一个最大的size给子view,最大的size也就是父view的size
    3.1)此时不论子view的为match_parent或者wrap_content,子view的specMode都为AT_MOST
    3.2)resultSize的大小被设置为父view的大小
4)当父view的specMode为UNSPECIFIED的时候:
    4.1) 此时不论子view的为match_parent或者wrap_content,子view的specMode都为UNSPECIFIED
    4.3)此时reusltSize = 0
5)从对UNSPECIFIED分析可以知道,在1)情况之外的情况下,要是想让子view自己决定自己的宽度或者高度的大小,MeasureSpec.makeMeasureSpec(0,UNSPECIFIED)这么调用来获取宽度和高度
的spec
6)根据方法的参数以及方法的实现可以知道,视图的大小是由父视图和子视图共同决定的,前提是子视图没有设置具体的dimen值

可以用下表来总结上面的结论,并作为此篇博客的总结:

猜你喜欢

转载自blog.csdn.net/suyimin2010/article/details/81295022