全网整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:400-708-3566

Android  MeasureSpec的理解和源码的解析

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); 
    } 
   
   
 
} 




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


# Android  # MeasureSpec  # MeasureSpec实例详解  # Android 软键盘状态并隐藏输入法的实例  # Android LocationManager获取经度与纬度等地理信息  # Android中Activity和Fragment传递数据的两种方式  # Android ListView之setEmptyView正确使用方法  # Android 开发之Dialog中隐藏键盘的正确使用方法  # Android Intent调用 Uri的方法总结  # 微信Android热更新Tinker使用详解(星空武哥)  # 也为  # 就为  # 为该  # 是一个  # 请注意  # 不确定  # 这个时候  # 地说  # 即为  # 文档  # 在此  # 已被  # 第二个  # 三种  # 时说  # 而定  # 不明确  # 的是  # 都不  # 过了 


相关文章: 淘宝制作网站有哪些,淘宝网官网主页?  电商平台网站制作流程,电商网站如何制作?  网站制作的步骤包括,正确网址格式怎么写?  非常酷的网站设计制作软件,酷培ai教育官方网站?  佛山网站制作系统,佛山企业变更地址网上办理步骤?  制作农业网站的软件,比较好的农业网站推荐一下?  如何快速搭建二级域名独立网站?  名字制作网站免费,所有小说网站的名字?  免费的流程图制作网站有哪些,2025年教师初级职称申报网上流程?  网站制作的软件有哪些,制作微信公众号除了秀米还有哪些比较好用的平台?  如何选择最佳自助建站系统?快速指南解析优劣  公司网站制作价格怎么算,公司办个官网需要多少钱?  网页设计与网站制作内容,怎样注册网站?  韩国代理服务器如何选?解析IP设置技巧与跨境访问优化指南  c# Task.Yield 的作用是什么 它和Task.Delay(1)有区别吗  建站之星代理商如何保障技术支持与售后服务?  如何通过FTP服务器快速搭建网站?  如何快速搭建高效可靠的建站解决方案?  建站之星价格显示格式升级,你的预算足够吗?  内部网站制作流程,如何建立公司内部网站?  免费ppt制作网站,有没有值得推荐的免费PPT网站?  如何在Mac上搭建Golang开发环境_使用Homebrew安装和管理Go版本  网站制作哪家好,cc、.co、.cm哪个域名更适合做网站?  如何通过西部数码建站助手快速创建专业网站?  网站制作公司哪里好做,成都网站制作公司哪家做得比较好,更正规?  如何破解联通资金短缺导致的基站建设难题?  如何快速查询网站的真实建站时间?  公司网站的制作公司,企业网站制作基本流程有哪些?  如何在万网自助建站中设置域名及备案?  如何通过服务器快速搭建网站?完整步骤解析  php能控制zigbee模块吗_php通过串口与cc2530 zigbee通信【介绍】  如何制作新型网站程序文件,新型止水鱼鳞网要拆除吗?  如何在万网自助建站平台快速创建网站?  高防服务器租用首荐平台,企业级优惠套餐快速部署  建站三合一如何选?哪家性价比更高?  Python多线程使用规范_线程安全解析【教程】  如何用美橙互联一键搭建多站合一网站?  网站制作培训多少钱一个月,网站优化seo培训课程有哪些?  制作网站的软件下载免费,今日头条开宝箱老是需要下载怎么回事?  深圳网站制作案例,网页的相关名词有哪些?  Android滚轮选择时间控件使用详解  如何在万网主机上快速搭建网站?  韩国网站服务器搭建指南:VPS选购、域名解析与DNS配置推荐  网站制作专业公司有哪些,如何制作一个企业网站,建设网站的基本步骤有哪些?  零服务器AI建站解决方案:快速部署与云端平台低成本实践  如何配置WinSCP新建站点的密钥验证步骤?  头像制作网站在线观看,除了站酷,还有哪些比较好的设计网站?  php条件判断怎么写_ifelse和switchcase的使用区别【对比】    魔方云NAT建站如何实现端口转发? 

您的项目需求

*请认真填写需求信息,我们会在24小时内与您取得联系。