总结来说 mode 取值 受父容器与子控件共同决定


在自定义控件时为了满足特定需求,widget大都是我们自己测量的。
大家都知道测量时最重要的步骤就是重写onMeasure方法,来计算出宽高。

这里面的MeasureSpec 很重要,大家也都知道,它是一个java中的静态类,它有重要的三个静态常量和三个最重要的静态方法。

我这里说下MeasureSpec 的3中模式 
UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小; 
EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小; 
AT_MOST(至多),子元素至多达到指定大小的值。 

看过很多博客,大家总结说,模式和XML布局有如下对应关系:
wrap_content-> MeasureSpec.AT_MOST
match_parent -> MeasureSpec.EXACTLY
50dp(确切值) -> MeasureSpec.EXACTLY

但是在具体开发中 我发现并不是一定这样,然后就看了下源码,才发现子控件的mode不只是与自身的设置有关系,它还受到父容器影响。

getChildMeasureSpec() 方法

 

[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.             // 父容器是 EXACTLY 模式  
  13.             case MeasureSpec.EXACTLY:  
  14.                 if (childDimension >= 0) {  
  15.                     // 如果 child 希望有自己的尺寸,那么允许它,并把它的测量模式设置为 EXACTLY  
  16.                     resultSize = childDimension;  
  17.                     resultMode = MeasureSpec.EXACTLY;  
  18.                 } else if (childDimension == LayoutParams.MATCH_PARENT) {  
  19.                     // Child wants to be our size. So be it.  
  20.                     // child 希望尺寸与 父容器 一样大,那么允许它  
  21.                     resultSize = size;  
  22.                     resultMode = MeasureSpec.EXACTLY;  
  23.                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {  
  24.                     // Child wants to determine its own size. It can't be  
  25.                     // bigger than us.  
  26.                     // child 希望由自己决定自己的尺寸,但是有个前提是它不能大于 父容器 本身给的建议值。  
  27.                     // 并且需要设置它的测量模式为 AT_MOST  
  28.                     resultSize = size;  
  29.                     resultMode = MeasureSpec.AT_MOST;  
  30.                 }  
  31.                 break;  
  32.   
  33.             // Parent has imposed a maximum size on us  
  34.             // 父容器是 AT_MOST 模式,对于 父容器 本身而言,它也有个最大的尺寸约束  
  35.             case MeasureSpec.AT_MOST:  
  36.                 if (childDimension >= 0) {  
  37.                     // Child wants a specific size... so be it  
  38.                     // 如果 child 希望有自己的尺寸,那么允许它,并把它的测量模式设置为 EXACTLY  
  39.                     resultSize = childDimension;  
  40.                     resultMode = MeasureSpec.EXACTLY;  
  41.                 } else if (childDimension == LayoutParams.MATCH_PARENT) {  
  42.                     // Child wants to be our size, but our size is not fixed.  
  43.                     // Constrain child to not be bigger than us.  
  44.                     // child 希望尺寸和 父容器 一样大,那么允许它,但是 父容器 本身有限制,所以也  
  45.                     // 需要给 child 加一个限制,这个限制就是将 child 模式设置为 AT_MOST  
  46.                     resultSize = size;  
  47.                     resultMode = MeasureSpec.AT_MOST;  
  48.                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {  
  49.                     // Child wants to determine its own size. It can't be  
  50.                     // bigger than us.  
  51.                     // child 希望由自己决定自己的尺寸,但是有个前提是它不能大于 父容器 本身给的建议值。  
  52.                     // 并且需要设置它的测量模式为 AT_MOST  
  53.                     resultSize = size;  
  54.                     resultMode = MeasureSpec.AT_MOST;  
  55.                 }  
  56.                 break;  
  57.   
  58.             // Parent asked to see how big we want to be  
  59.             // 父容器是 UNSPECIFIED 模式,对于 父容器 本身而言,它的期望值是想要多大就多大,让 父容器 的 parent不要限制它。  
  60.             case MeasureSpec.UNSPECIFIED:  
  61.                 if (childDimension >= 0) {  
  62.                     // Child wants a specific size... let him have it  
  63.                     //如果 child 希望有自己的尺寸,那么允许它,并把它的测量模式设置为 EXACTLY  
  64.                     resultSize = childDimension;  
  65.                     resultMode = MeasureSpec.EXACTLY;  
  66.                 } else if (childDimension == LayoutParams.MATCH_PARENT) {  
  67.                     // Child wants to be our size... find out how big it should  
  68.                     // be  
  69.                     // child 希望尺寸和 父容器 一样大,那么允许它,但是 父容器 本身也需要计算,所以只能设置为0,并且  
  70.                     // 将child的测量模式设置为 UNSPECIFIED  
  71.                     resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;  
  72.                     resultMode = MeasureSpec.UNSPECIFIED;  
  73.                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {  
  74.                     // Child wants to determine its own size.... find out how  
  75.                     // big it should be  
  76.                     // child 希望尺寸由自己决定,一般这个时候,父容器 会给它一个 Size 作为最大值限制,  
  77.                     // 但是 父容器 本身也需要计算,所以只能设置为0,并且没有给child最大值的设定  
  78.                     // 将child的测量模式设置为 UNSPECIFIED  
  79.                     resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;  
  80.                     resultMode = MeasureSpec.UNSPECIFIED;  
  81.                 }  
  82.                 break;  
  83.         }  
  84.         //noinspection ResourceType  
  85.         return MeasureSpec.makeMeasureSpec(resultSize, resultMode);  
  86.     }  


总结来说 mode 取值 受父容器与子控件共同决定

父容器是MeasureSpec.EXACTLY模式 
   子控件是具体值, mode = MeasureSpec.EXACTLY
   子控件是MATCH_PARENT, mode = MeasureSpec.EXACTLY
   子控件是WRAP_CONTENT, mode = MeasureSpec.AT_MOST

父容器是MeasureSpec.AT_MOST模式 
   子控件是具体值, mode = MeasureSpec.EXACTLY
   子控件是MATCH_PARENT, mode = MeasureSpec.AT_MOST
   子控件是WRAP_CONTENT, mode = MeasureSpec.AT_MOST

父容器是MeasureSpec.UNSPECIFIED模式 
   子控件是具体值, mode = MeasureSpec.EXACTLY
   子控件是MATCH_PARENT, mode = MeasureSpec.UNSPECIFIED
   子控件是WRAP_CONTENT, mode = MeasureSpec.UNSPECIFIED

 

就这样

posted on 2017-09-28 16:11  猪猪一号  阅读(202)  评论(0编辑  收藏  举报

导航