当前位置: 移动技术网 > IT编程>移动开发>Android > Android MeasureSpec的理解和源码的解析

Android MeasureSpec的理解和源码的解析

2019年07月24日  | 移动技术网IT编程  | 我要评论

oh必胜奉顺英国语,x3100m4,ipv6

android  measurespec的理解和源码的解析

measurespec的创建规则:

实例详解:

package cc.ww; 
 
import android.view.view; 
import android.view.view.measurespec; 
import android.view.viewgroup.layoutparams; 
import android.view.viewgroup.marginlayoutparams; 
import android.widget.linearlayout; 
 
/** 
 * @author http://blog.csdn.net/lfdfhl 
 * 
 * 文档描述: 
 * 关于measurespec的理解 
 * 
 * (1) measurespec基础知识 
 *   measurespec通常翻译为"测量规格",它是一个32位的int数据. 
 *   其中高2位代表specmode即某种测量模式,低32位为specsize代表在该模式下的规格大小. 
 *   可以通过:  
 *   int specmode = measurespec.getmode(measurespec) 获取specmode 
    int specsize = measurespec.getsize(measurespec) 获取specsize 
    
      常用的specmode有三种: 
      
    measurespec.exactly 
      官方文档 
    measure specification mode: the parent has determined an exact size 
    for the child. the child is going to be given those bounds regardless of how big it wants to be. 
         父容器已经检测出子view所需要的精确大小.该子view最终的测量大小即为specsize. 
    (1) 当子view的layoutparams的宽(高)采用具体的值(如100px)时且父容器的measurespec为 measurespec.exactly或者 
    measurespec.at_most或者measurespec.unspecified时: 
         系统返回给该子view的specmode就为 measurespec.exactly 
         系统返回给该子view的specsize就为子view自己指定的大小(childsize) 
         通俗地理解: 
         子view的layoutparams的宽(高)采用具体的值(如100px)时,那么说明该子view的大小是非常明确的,明确到已经用具体px值 
         指定的地步了.那么此时不管父容器的specmode是什么,系统返回给该子view的specmode总是measurespec.exactly,并且 
         系统返回给该子view的specsize就为子view自己指定的大小(childsize). 
    
    (2) 当子view的layoutparams的宽(高)采用match_parent时并且父容器的measurespec为 measurespec.exactly时: 
         系统返回给该子view的specmode就为 measurespec.exactly 
         系统返回给该子view的specsize就为该父容器剩余空间的大小(parentleftsize) 
         通俗地理解: 
      子view的layoutparams的宽(高)采用match_parent时并且父容器的measurespec为 measurespec.exactly. 
      这时候说明子view的大小还是挺明确的:就是要和父容器一样大,更加直白地说就是父容器要怎样子view就要怎样. 
      所以,如果父容器measurespec为 measurespec.exactly那么: 
      系统返回给该子view的specmode就为 measurespec.exactly,和父容器一样. 
         系统返回给该子view的specsize就为该父容器剩余空间的大小(parentleftsize),就是父容器的剩余大小. 
         同样的道理如果此时,measurespec为 measurespec.at_most呢? 
         系统返回给该子view的specmode也为 measurespec.at_most,和父容器一样. 
         系统返回给该子view的specsize也为该父容器剩余空间的大小(parentleftsize),就是父容器的剩余大小. 
      
    measurespec.at_most 
      官方文档 
    the child can be as large as it wants up to the specified size. 
      父容器指定了一个可用大小即specsize,子view的大小不能超过该值. 
    (1) 当子view的layoutparams的宽(高)采用match_parent时并且父容器的measurespec为 measurespec.at_most时: 
         系统返回给该子view的specmode就为 measurespec.at_most 
         系统返回给该子view的specsize就为该父容器剩余空间的大小(parentleftsize) 
         这种情况已经在上面介绍 measurespec.exactly时已经讨论过了. 
   (2) 当子view的layoutparams的宽(高)采用wrap_content时并且父容器的measurespec为 measurespec.exactly时: 
         系统返回给该子view的specmode就为 measurespec.at_most 
         系统返回给该子view的specsize就为该父容器剩余空间的大小(parentleftsize) 
         通俗地理解: 
         子view的layoutparams的宽(高)采用wrap_content时说明这个子view的宽高不明确,要视content而定. 
         这个时候如果父容器的measurespec为 measurespec.exactly即父容器是一个精确模式;这个时候简单地说 
         子view是不确定的,父容器是确定的,那么 
         系统返回给该子view的specmode也就是不确定的即为 measurespec.at_most 
         系统返回给该子view的specsize就为该父容器剩余空间的大小(parentleftsize) 
    (3) 当子view的layoutparams的宽(高)采用wrap_content时并且父容器的measurespec为 measurespec.at_most时: 
         系统返回给该子view的specmode就为 measurespec.at_most 
         系统返回给该子view的specsize就为该父容器剩余空间的大小(parentleftsize) 
         通俗地理解: 
         子view的layoutparams的宽(高)采用wrap_content时说明这个子view的宽高不明确,要视content而定. 
         这个时候如果父容器的measurespec为 measurespec.at_most这个时候简单地说 
         子view是不确定的,父容器也是不确定的,那么 
         系统返回给该子view的specmode也就是不确定的即为 measurespec.at_most 
         系统返回给该子view的specsize就为该父容器剩余空间的大小(parentleftsize) 
         
    
    measurespec.unspecified 
      官方文档 
    the parent has not imposed any constraint on the child. it can be whatever size it wants. 
      父容器不对子view的大小做限制. 
      一般用作android系统内部,或者listview和scrollview.在此不做讨论. 
      
      关于这个三种测量规格下面的源码分析中体现得很明显,也可参考以下附图. 
    
 * (2) 在onmeasure()时子view的measurespec的形成过程分析 
 *   关于该技术点的讨论,请看下面的源码分析. 
 * 
 */ 
public class understandmeasurespec { 
   
  /** 
   * 第一步: 
   * 在viewgroup测量子view时会调用到measurechildwithmargins()方法,或者与之类似的方法. 
   * 请注意方法的参数: 
   * @param child 
   * 子view 
   * @param parentwidthmeasurespec 
   * 父容器(比如linearlayout)的宽的measurespec 
   * @param widthused 
   * 父容器(比如linearlayout)在水平方向已经占用的空间大小 
   * @param parentheightmeasurespec 
   * 父容器(比如linearlayout)的高的measurespec 
   * @param heightused 
   * 父容器(比如linearlayout)在垂直方向已经占用的空间大小 
   * 
   * 在该方法中主要有四步操作,其中很重要的是调用了getchildmeasurespec()方法来确定 
   * 子view的measurespec.详情参见代码分析 
   */ 
  protected void measurechildwithmargins(view child,int parentwidthmeasurespec, int widthused, 
                           int parentheightmeasurespec, int heightused) { 
    //1 得到子view的layoutparams 
    final marginlayoutparams lp = (marginlayoutparams) child.getlayoutparams(); 
    //2 得到子view的宽的measurespec 
    final int childwidthmeasurespec = getchildmeasurespec 
    (parentwidthmeasurespec,mpaddingleft + mpaddingright + lp.leftmargin + lp.rightmargin + widthused, lp.width); 
    //3 得到子view的高的measurespec 
    final int childheightmeasurespec = getchildmeasurespec 
    (parentheightmeasurespec,mpaddingtop + mpaddingbottom + lp.topmargin + lp.bottommargin + heightused, lp.height); 
    //4 测量子view 
    child.measure(childwidthmeasurespec, childheightmeasurespec); 
  } 
   
   
  /** 
   * getchildmeasurespec()方法确定子view的measurespec 
   * 请注意方法的参数: 
   * @param spec 
   * 父容器(比如linearlayout)的宽或高的measurespec 
   * @param padding 
   * 父容器(比如linearlayout)在垂直方向或者水平方向已被占用的空间. 
   * 在measurechildwithmargins()方法里调用getchildmeasurespec()时注意第二个参数的构成: 
   * 比如:mpaddingleft + mpaddingright + lp.leftmargin + lp.rightmargin 
   * 其中: 
   * mpaddingleft和mpaddingright表示父容器左右两内侧的padding 
   * lp.leftmargin和lp.rightmargin表示子view左右两外侧的margin 
   * 这四部分都不可以再利用起来布局子view.所以说这些值的和表示: 
   * 父容器在水平方向已经被占用的空间 
   * 同理: 
   * mpaddingtop + mpaddingbottom + lp.topmargin + lp.bottommargin 
   * 表示: 
   * 父容器(比如linearlayout)在垂直方向已被占用的空间. 
   * @param childdimension 
   * 通过子view的layoutparams获取到的子view的宽或高 
   * 
   * 
   * 经过以上分析可从getchildmeasurespec()方法的第一个参数和第二个参数可以得出一个结论: 
   * 父容器(如linearlayout)的measurespec和子view的layoutparams共同决定了子view的measurespec!!! 
   * 
   * 
   * 
   */ 
   public static int getchildmeasurespec(int spec, int padding, int childdimension) { 
      /** 
       * 第一步:得到父容器的specmode和specsize 
       */ 
      int specmode = measurespec.getmode(spec); 
      int specsize = measurespec.getsize(spec); 
      /** 
       * 第二步:得到父容器在水平方向或垂直方向可用的最大空间值. 
       *    关于padding参见上面的分析 
       */ 
      int size = math.max(0, specsize - padding); 
 
      int resultsize = 0; 
      int resultmode = 0; 
 
       
      /** 
       * 第三步:确定子view的specmode和specsize. 
       *    在此分为三种情况进行. 
       */ 
      switch (specmode) { 
      /** 
       * 第一种情况: 
       * 父容器的测量模式为exactly 
       * 
       * 请注意两个系统常量: 
       * layoutparams.match_parent=-1 
       * layoutparams.wrap_content=-2 
       * 所以在此处的代码: 
       * childdimension >= 0 表示子view的宽或高不是match_parent和wrap_content 
       */ 
      case measurespec.exactly: 
        /** 
         * 当父容器的测量模式为exactly时如果: 
         * 子view的宽或高是一个精确的值,比如100px; 
         * 那么: 
         * 子view的size就是childdimension 
         * 子view的mode也为measurespec.exactly 
         */ 
        if (childdimension >= 0) { 
          resultsize = childdimension; 
          resultmode = measurespec.exactly; 
        /** 
         * 当父容器的测量模式为exactly时如果: 
         * 子view的宽或高是layoutparams.match_parent 
         * 那么: 
         * 子view的size就是父容器在水平方向或垂直方向可用的最大空间值即size 
         * 子view的mode也为measurespec.exactly 
         */ 
        } else if (childdimension == layoutparams.match_parent) { 
          // child wants to be our size. so be it. 
          resultsize = size; 
          resultmode = measurespec.exactly; 
        /** 
         * 当父容器的测量模式为exactly时如果: 
         * 子view的宽或高是layoutparams.wrap_content 
         * 那么: 
         * 子view的size就是父容器在水平方向或垂直方向可用的最大空间值即size 
         * 子view的mode为measurespec.at_most 
         */ 
        } else if (childdimension == layoutparams.wrap_content) { 
          // child wants to determine its own size. it can't be bigger than us. 
          resultsize = size; 
          resultmode = measurespec.at_most; 
        } 
        break; 
 
      /** 
       * 第二种情况: 
       * 父容器的测量模式为at_most 
       * 
       * 请注意两个系统常量:pp 
       * layoutparams.match_parent=-1 
       * layoutparams.wrap_content=-2 
       * 所以在此处的代码: 
       * childdimension >= 0 表示子view的宽或高不是match_parent和wrap_content 
       */ 
      case measurespec.at_most: 
        /** 
         * 当父容器的测量模式为at_most时如果: 
         * 子view的宽或高是一个精确的值,比如100px; 
         * 那么: 
         * 子view的size就是childdimension 
         * 子view的mode也为measurespec.exactly 
         */ 
        if (childdimension >= 0) { 
          // child wants a specific size... so be it 
          resultsize = childdimension; 
          resultmode = measurespec.exactly; 
        /** 
         * 当父容器的测量模式为at_most时如果: 
         * 子view的宽或高为layoutparams.match_parent 
         * 那么: 
         * 子view的size就是父容器在水平方向或垂直方向可用的最大空间值即size 
         * 子view的mode也为measurespec.at_most 
         */ 
        } else if (childdimension == layoutparams.match_parent) { 
          // child wants to be our size, but our size is not fixed. 
          // constrain child to not be bigger than us. 
          resultsize = size; 
          resultmode = measurespec.at_most; 
         /** 
         * 当父容器的测量模式为at_most时如果: 
         * 子view的宽或高为layoutparams.wrap_content 
         * 那么: 
         * 子view的size就是父容器在水平方向或垂直方向可用的最大空间值即size 
         * 子view的mode也为measurespec.at_most 
         */ 
        } else if (childdimension == layoutparams.wrap_content) { 
          // child wants to determine its own size. it can't be 
          // bigger than us. 
          resultsize = size; 
          resultmode = measurespec.at_most; 
        } 
        break; 
 
      /** 
       * 第三种情况: 
       * 父容器的测量模式为unspecified 
       * 
       * 请注意两个系统常量: 
       * layoutparams.match_parent=-1 
       * layoutparams.wrap_content=-2 
       * 所以在此处的代码: 
       * childdimension >= 0 表示子view的宽或高不是match_parent和wrap_content 
       */ 
      case measurespec.unspecified: 
        /** 
         * 当父容器的测量模式为unspecified时如果: 
         * 子view的宽或高是一个精确的值,比如100px; 
         * 那么: 
         * 子view的size就是childdimension 
         * 子view的mode也为measurespec.exactly 
         */ 
        if (childdimension >= 0) { 
          // child wants a specific size... let him have it 
          resultsize = childdimension; 
          resultmode = measurespec.exactly; 
        /** 
         * 当父容器的测量模式为unspecified时如果: 
         * 子view的宽或高为layoutparams.match_parent 
         * 那么: 
         * 子view的size为0 
         * 子view的mode也为measurespec.unspecified 
         */ 
        } else if (childdimension == layoutparams.match_parent) { 
          // child wants to be our size... find out how big it should be 
          resultsize = 0; 
          resultmode = measurespec.unspecified; 
        /** 
         * 当父容器的测量模式为unspecified时如果: 
         * 子view的宽或高为layoutparams.wrap_content 
         * 那么: 
         * 子view的size为0 
         * 子view的mode也为measurespec.unspecified 
         */ 
        } else if (childdimension == layoutparams.wrap_content) { 
          // child wants to determine its own size.... find out how big it should be 
          resultsize = 0; 
          resultmode = measurespec.unspecified; 
        } 
        break; 
      } 
      return measurespec.makemeasurespec(resultsize, resultmode); 
    } 
   
   
 
} 




 如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网