MeasureSpec的理解和详尽源码分析

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package cc.ww;  
  2.   
  3. import android.view.View;  
  4. import android.view.View.MeasureSpec;  
  5. import android.view.ViewGroup.LayoutParams;  
  6. import android.view.ViewGroup.MarginLayoutParams;  
  7. import android.widget.LinearLayout;  
  8.   
  9. /** 
  10.  * @author http://blog.csdn.net/lfdfhl 
  11.  *  
  12.  * 文档描述: 
  13.  * 关于MeasureSpec的理解 
  14.  *  
  15.  * (1) MeasureSpec基础知识 
  16.  *     MeasureSpec通常翻译为"测量规格",它是一个32位的int数据. 
  17.  *     其中高2位代表SpecMode即某种测量模式,低32位为SpecSize代表在该模式下的规格大小. 
  18.  *     可以通过:   
  19.  *     int specMode = MeasureSpec.getMode(measureSpec) 获取specMode 
  20.        int specSize = MeasureSpec.getSize(measureSpec) 获取SpecSize 
  21.         
  22.            常用的SpecMode有三种: 
  23.             
  24.        MeasureSpec.EXACTLY 
  25.            官方文档 
  26.        Measure specification mode: The parent has determined an exact size 
  27.        for the child. The child is going to be given those bounds regardless of how big it wants to be. 
  28.                   父容器已经检测出子View所需要的精确大小.该子View最终的测量大小即为SpecSize. 
  29.        (1) 当子View的LayoutParams的宽(高)采用具体的值(如100px)时且父容器的MeasureSpec为 MeasureSpec.EXACTLY或者 
  30.        MeasureSpec.AT_MOST或者MeasureSpec.UNSPECIFIED时: 
  31.                   系统返回给该子View的specMode就为 MeasureSpec.EXACTLY 
  32.                   系统返回给该子View的specSize就为子View自己指定的大小(childSize) 
  33.                   通俗地理解: 
  34.                   子View的LayoutParams的宽(高)采用具体的值(如100px)时,那么说明该子View的大小是非常明确的,明确到已经用具体px值 
  35.                   指定的地步了.那么此时不管父容器的specMode是什么,系统返回给该子View的specMode总是MeasureSpec.EXACTLY,并且 
  36.                   系统返回给该子View的specSize就为子View自己指定的大小(childSize). 
  37.         
  38.        (2) 当子View的LayoutParams的宽(高)采用match_parent时并且父容器的MeasureSpec为 MeasureSpec.EXACTLY时: 
  39.                   系统返回给该子View的specMode就为 MeasureSpec.EXACTLY 
  40.                   系统返回给该子View的specSize就为该父容器剩余空间的大小(parentLeftSize) 
  41.                   通俗地理解: 
  42.            子View的LayoutParams的宽(高)采用match_parent时并且父容器的MeasureSpec为 MeasureSpec.EXACTLY. 
  43.            这时候说明子View的大小还是挺明确的:就是要和父容器一样大,更加直白地说就是父容器要怎样子View就要怎样. 
  44.            所以,如果父容器MeasureSpec为 MeasureSpec.EXACTLY那么: 
  45.            系统返回给该子View的specMode就为 MeasureSpec.EXACTLY,和父容器一样. 
  46.                  系统返回给该子View的specSize就为该父容器剩余空间的大小(parentLeftSize),就是父容器的剩余大小. 
  47.                   同样的道理如果此时,MeasureSpec为 MeasureSpec.AT_MOST呢? 
  48.                  系统返回给该子View的specMode也为 MeasureSpec.AT_MOST,和父容器一样. 
  49.                  系统返回给该子View的specSize也为该父容器剩余空间的大小(parentLeftSize),就是父容器的剩余大小. 
  50.            
  51.        MeasureSpec.AT_MOST 
  52.            官方文档 
  53.        The child can be as large as it wants up to the specified size. 
  54.             父容器指定了一个可用大小即specSize,子View的大小不能超过该值. 
  55.        (1) 当子View的LayoutParams的宽(高)采用match_parent时并且父容器的MeasureSpec为 MeasureSpec.AT_MOST时: 
  56.                   系统返回给该子View的specMode就为 MeasureSpec.AT_MOST 
  57.                   系统返回给该子View的specSize就为该父容器剩余空间的大小(parentLeftSize) 
  58.                   这种情况已经在上面介绍 MeasureSpec.EXACTLY时已经讨论过了. 
  59.       (2) 当子View的LayoutParams的宽(高)采用wrap_content时并且父容器的MeasureSpec为 MeasureSpec.EXACTLY时: 
  60.                   系统返回给该子View的specMode就为 MeasureSpec.AT_MOST 
  61.                   系统返回给该子View的specSize就为该父容器剩余空间的大小(parentLeftSize) 
  62.                   通俗地理解: 
  63.                   子View的LayoutParams的宽(高)采用wrap_content时说明这个子View的宽高不明确,要视content而定. 
  64.                   这个时候如果父容器的MeasureSpec为 MeasureSpec.EXACTLY即父容器是一个精确模式;这个时候简单地说 
  65.                   子View是不确定的,父容器是确定的,那么 
  66.                   系统返回给该子View的specMode也就是不确定的即为 MeasureSpec.AT_MOST 
  67.                   系统返回给该子View的specSize就为该父容器剩余空间的大小(parentLeftSize) 
  68.        (3) 当子View的LayoutParams的宽(高)采用wrap_content时并且父容器的MeasureSpec为 MeasureSpec.AT_MOST时: 
  69.                   系统返回给该子View的specMode就为 MeasureSpec.AT_MOST 
  70.                   系统返回给该子View的specSize就为该父容器剩余空间的大小(parentLeftSize) 
  71.                   通俗地理解: 
  72.                   子View的LayoutParams的宽(高)采用wrap_content时说明这个子View的宽高不明确,要视content而定. 
  73.                   这个时候如果父容器的MeasureSpec为 MeasureSpec.AT_MOST这个时候简单地说 
  74.                   子View是不确定的,父容器也是不确定的,那么 
  75.                   系统返回给该子View的specMode也就是不确定的即为 MeasureSpec.AT_MOST 
  76.                   系统返回给该子View的specSize就为该父容器剩余空间的大小(parentLeftSize) 
  77.                   
  78.         
  79.        MeasureSpec.UNSPECIFIED 
  80.            官方文档 
  81.        The parent has not imposed any constraint on the child. It can be whatever size it wants. 
  82.            父容器不对子View的大小做限制. 
  83.            一般用作Android系统内部,或者ListView和ScrollView.在此不做讨论. 
  84.             
  85.            关于这个三种测量规格下面的源码分析中体现得很明显,也可参考以下附图. 
  86.         
  87.  * (2) 在onMeasure()时子View的MeasureSpec的形成过程分析 
  88.  *     关于该技术点的讨论,请看下面的源码分析. 
  89.  * 
  90.  */  
  91. public class UnderstandMeasureSpec {  
  92.       
  93.     /** 
  94.      * 第一步: 
  95.      * 在ViewGroup测量子View时会调用到measureChildWithMargins()方法,或者与之类似的方法. 
  96.      * 请注意方法的参数: 
  97.      * @param child 
  98.      * 子View 
  99.      * @param parentWidthMeasureSpec 
  100.      * 父容器(比如LinearLayout)的宽的MeasureSpec 
  101.      * @param widthUsed 
  102.      * 父容器(比如LinearLayout)在水平方向已经占用的空间大小 
  103.      * @param parentHeightMeasureSpec 
  104.      * 父容器(比如LinearLayout)的高的MeasureSpec 
  105.      * @param heightUsed 
  106.      * 父容器(比如LinearLayout)在垂直方向已经占用的空间大小 
  107.      *  
  108.      * 在该方法中主要有四步操作,其中很重要的是调用了getChildMeasureSpec()方法来确定 
  109.      * 子View的MeasureSpec.详情参见代码分析 
  110.      */  
  111.     protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,  
  112.                                                       int parentHeightMeasureSpec, int heightUsed) {  
  113.         //1 得到子View的LayoutParams  
  114.         final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();  
  115.         //2 得到子View的宽的MeasureSpec  
  116.         final int childWidthMeasureSpec = getChildMeasureSpec  
  117.         (parentWidthMeasureSpec,mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width);  
  118.         //3 得到子View的高的MeasureSpec  
  119.         final int childHeightMeasureSpec = getChildMeasureSpec  
  120.         (parentHeightMeasureSpec,mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height);  
  121.         //4 测量子View  
  122.         child.measure(childWidthMeasureSpec, childHeightMeasureSpec);  
  123.     }  
  124.       
  125.       
  126.     /** 
  127.      * getChildMeasureSpec()方法确定子View的MeasureSpec 
  128.      * 请注意方法的参数: 
  129.      * @param spec 
  130.      * 父容器(比如LinearLayout)的宽或高的MeasureSpec 
  131.      * @param padding 
  132.      * 父容器(比如LinearLayout)在垂直方向或者水平方向已被占用的空间. 
  133.      * 在measureChildWithMargins()方法里调用getChildMeasureSpec()时注意第二个参数的构成: 
  134.      * 比如:mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin 
  135.      * 其中: 
  136.      * mPaddingLeft和mPaddingRight表示父容器左右两内侧的padding 
  137.      * lp.leftMargin和lp.rightMargin表示子View左右两外侧的margin 
  138.      * 这四部分都不可以再利用起来布局子View.所以说这些值的和表示: 
  139.      * 父容器在水平方向已经被占用的空间 
  140.      * 同理: 
  141.      * mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin 
  142.      * 表示: 
  143.      * 父容器(比如LinearLayout)在垂直方向已被占用的空间. 
  144.      * @param childDimension 
  145.      * 通过子View的LayoutParams获取到的子View的宽或高 
  146.      *  
  147.      *  
  148.      * 经过以上分析可从getChildMeasureSpec()方法的第一个参数和第二个参数可以得出一个结论: 
  149.      * 父容器(如LinearLayout)的MeasureSpec和子View的LayoutParams共同决定了子View的MeasureSpec!!! 
  150.      *  
  151.      *  
  152.      *  
  153.      */  
  154.      public static int getChildMeasureSpec(int spec, int padding, int childDimension) {  
  155.             /** 
  156.              * 第一步:得到父容器的specMode和specSize 
  157.              */  
  158.             int specMode = MeasureSpec.getMode(spec);  
  159.             int specSize = MeasureSpec.getSize(spec);  
  160.             /** 
  161.              * 第二步:得到父容器在水平方向或垂直方向可用的最大空间值. 
  162.              *        关于padding参见上面的分析 
  163.              */  
  164.             int size = Math.max(0, specSize - padding);  
  165.   
  166.             int resultSize = 0;  
  167.             int resultMode = 0;  
  168.   
  169.               
  170.             /** 
  171.              * 第三步:确定子View的specMode和specSize. 
  172.              *        在此分为三种情况进行. 
  173.              */  
  174.             switch (specMode) {  
  175.             /** 
  176.              * 第一种情况: 
  177.              * 父容器的测量模式为EXACTLY 
  178.              *  
  179.              * 请注意两个系统常量: 
  180.              * LayoutParams.MATCH_PARENT=-1 
  181.              * LayoutParams.WRAP_CONTENT=-2 
  182.              * 所以在此处的代码: 
  183.              * childDimension >= 0 表示子View的宽或高不是MATCH_PARENT和WRAP_CONTENT 
  184.              */  
  185.             case MeasureSpec.EXACTLY:  
  186.                 /** 
  187.                  * 当父容器的测量模式为EXACTLY时如果: 
  188.                  * 子View的宽或高是一个精确的值,比如100px; 
  189.                  * 那么: 
  190.                  * 子View的size就是childDimension 
  191.                  * 子View的mode也为MeasureSpec.EXACTLY 
  192.                  */  
  193.                 if (childDimension >= 0) {  
  194.                     resultSize = childDimension;  
  195.                     resultMode = MeasureSpec.EXACTLY;  
  196.                 /** 
  197.                  * 当父容器的测量模式为EXACTLY时如果: 
  198.                  * 子View的宽或高是LayoutParams.MATCH_PARENT 
  199.                  * 那么: 
  200.                  * 子View的size就是父容器在水平方向或垂直方向可用的最大空间值即size 
  201.                  * 子View的mode也为MeasureSpec.EXACTLY 
  202.                  */  
  203.                 } else if (childDimension == LayoutParams.MATCH_PARENT) {  
  204.                     // Child wants to be our size. So be it.  
  205.                     resultSize = size;  
  206.                     resultMode = MeasureSpec.EXACTLY;  
  207.                 /** 
  208.                  * 当父容器的测量模式为EXACTLY时如果: 
  209.                  * 子View的宽或高是LayoutParams.WRAP_CONTENT 
  210.                  * 那么: 
  211.                  * 子View的size就是父容器在水平方向或垂直方向可用的最大空间值即size 
  212.                  * 子View的mode为MeasureSpec.AT_MOST 
  213.                  */  
  214.                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {  
  215.                     // Child wants to determine its own size. It can't be bigger than us.  
  216.                     resultSize = size;  
  217.                     resultMode = MeasureSpec.AT_MOST;  
  218.                 }  
  219.                 break;  
  220.   
  221.             /** 
  222.              * 第二种情况: 
  223.              * 父容器的测量模式为AT_MOST 
  224.              *  
  225.              * 请注意两个系统常量:pp 
  226.              * LayoutParams.MATCH_PARENT=-1 
  227.              * LayoutParams.WRAP_CONTENT=-2 
  228.              * 所以在此处的代码: 
  229.              * childDimension >= 0 表示子View的宽或高不是MATCH_PARENT和WRAP_CONTENT 
  230.              */  
  231.             case MeasureSpec.AT_MOST:  
  232.                 /** 
  233.                  * 当父容器的测量模式为AT_MOST时如果: 
  234.                  * 子View的宽或高是一个精确的值,比如100px; 
  235.                  * 那么: 
  236.                  * 子View的size就是childDimension 
  237.                  * 子View的mode也为MeasureSpec.EXACTLY 
  238.                  */  
  239.                 if (childDimension >= 0) {  
  240.                     // Child wants a specific size... so be it  
  241.                     resultSize = childDimension;  
  242.                     resultMode = MeasureSpec.EXACTLY;  
  243.                 /** 
  244.                   * 当父容器的测量模式为AT_MOST时如果: 
  245.                   * 子View的宽或高为LayoutParams.MATCH_PARENT 
  246.                   * 那么: 
  247.                   * 子View的size就是父容器在水平方向或垂直方向可用的最大空间值即size 
  248.                   * 子View的mode也为MeasureSpec.AT_MOST 
  249.                   */  
  250.                 } else if (childDimension == LayoutParams.MATCH_PARENT) {  
  251.                     // Child wants to be our size, but our size is not fixed.  
  252.                     // Constrain child to not be bigger than us.  
  253.                     resultSize = size;  
  254.                     resultMode = MeasureSpec.AT_MOST;  
  255.                  /** 
  256.                   * 当父容器的测量模式为AT_MOST时如果: 
  257.                   * 子View的宽或高为LayoutParams.WRAP_CONTENT 
  258.                   * 那么: 
  259.                   * 子View的size就是父容器在水平方向或垂直方向可用的最大空间值即size 
  260.                   * 子View的mode也为MeasureSpec.AT_MOST 
  261.                   */  
  262.                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {  
  263.                     // Child wants to determine its own size. It can't be  
  264.                     // bigger than us.  
  265.                     resultSize = size;  
  266.                     resultMode = MeasureSpec.AT_MOST;  
  267.                 }  
  268.                 break;  
  269.   
  270.             /** 
  271.               * 第三种情况: 
  272.               * 父容器的测量模式为UNSPECIFIED 
  273.               *  
  274.               * 请注意两个系统常量: 
  275.               * LayoutParams.MATCH_PARENT=-1 
  276.               * LayoutParams.WRAP_CONTENT=-2 
  277.               * 所以在此处的代码: 
  278.               * childDimension >= 0 表示子View的宽或高不是MATCH_PARENT和WRAP_CONTENT 
  279.               */  
  280.             case MeasureSpec.UNSPECIFIED:  
  281.                 /** 
  282.                  * 当父容器的测量模式为UNSPECIFIED时如果: 
  283.                  * 子View的宽或高是一个精确的值,比如100px; 
  284.                  * 那么: 
  285.                  * 子View的size就是childDimension 
  286.                  * 子View的mode也为MeasureSpec.EXACTLY 
  287.                  */  
  288.                 if (childDimension >= 0) {  
  289.                     // Child wants a specific size... let him have it  
  290.                     resultSize = childDimension;  
  291.                     resultMode = MeasureSpec.EXACTLY;  
  292.                 /** 
  293.                  * 当父容器的测量模式为UNSPECIFIED时如果: 
  294.                  * 子View的宽或高为LayoutParams.MATCH_PARENT 
  295.                  * 那么: 
  296.                  * 子View的size为0 
  297.                  * 子View的mode也为MeasureSpec.UNSPECIFIED 
  298.                  */  
  299.                 } else if (childDimension == LayoutParams.MATCH_PARENT) {  
  300.                     // Child wants to be our size... find out how big it should be  
  301.                     resultSize = 0;  
  302.                     resultMode = MeasureSpec.UNSPECIFIED;  
  303.                 /** 
  304.                  * 当父容器的测量模式为UNSPECIFIED时如果: 
  305.                  * 子View的宽或高为LayoutParams.WRAP_CONTENT 
  306.                  * 那么: 
  307.                  * 子View的size为0 
  308.                  * 子View的mode也为MeasureSpec.UNSPECIFIED 
  309.                  */  
  310.                 } else if (childDimension == LayoutParams.WRAP_CONTENT) {  
  311.                     // Child wants to determine its own size.... find out how big it should be  
  312.                     resultSize = 0;  
  313.                     resultMode = MeasureSpec.UNSPECIFIED;  
  314.                 }  
  315.                 break;  
  316.             }  
  317.             return MeasureSpec.makeMeasureSpec(resultSize, resultMode);  
  318.         }  
  319.       
  320.       
  321.   
  322. }  







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值