DevMeteor 2021-03-19 12:16 采纳率: 0%
浏览 16

为什么SpecMode是高两位而不是低两位

为什么安卓MeasureSpec中SpecMode是高两位而不是低两位

  • 写回答

1条回答 默认 最新

  • 字节卷动 博客专家认证 2021-03-19 15:34
    关注

    在 Android 开发中,我们也时常见到位运算的身影。在进行自定义 View 的时候,都会用到 int makeMeasureSpec(int size, int mode) 方法去获取 View 的尺寸和测量模式,那么它是怎么把两个变量组装成一个的呢?简单地讲就是用一个 32 位二进制数字中的高两位来存储测量模式 MeasureMode,用低 30 位来存储尺寸 MeasureSize,MeasureSpec 是 android.view.View 类中的一个内部类,关键代码如下:

     

    /**  
     * 三种SpecMode: 
     * 1.UNSPECIFIED 
     * 父 ViewGroup 没有对子View施加任何约束,子 view 可以是任意大小。这种情况比较少见,主要用于系统内部多次measure的情形,
     * 用到的一般都是可以滚动的容器中的子view,比如ListView、GridView、RecyclerView中某些情况下的子view就是这种模式。
     * 一般来说,我们不需要关注此模式。
     * 2.EXACTLY 
     * 该 view 必须使用父 ViewGroup 给其指定的尺寸。对应 match_parent 或者具体数值(比如30dp)
     * 3.AT_MOST 
     * 该 View 最大可以取父ViewGroup给其指定的尺寸。对应wrap_content
     *  
     * MeasureSpec使用了二进制去减少对象的分配。 
     */  
    public class MeasureSpec {  
            // 进位大小为2的30次方(int的大小为32位,所以进位30位就是要使用int的最高位和第二高位也就是32和31位做标志位)  
            private static final int MODE_SHIFT = 30;  
     
            // 运算遮罩,0x3为16进制,10进制为3,二进制为11。3向左进位30,就是11 00000000000(11后跟30个0)  
            // (遮罩的作用是用1标注需要的值,0标注不要的值。因为1与任何数做与运算都得任何数,0与任何数做与运算都得0)  
            private static final int MODE_MASK  = 0x3 << MODE_SHIFT;  
     
            // 0向左进位30,就是00 00000000000(00后跟30个0)  
            public static final int UNSPECIFIED = 0 << MODE_SHIFT;  
            // 1向左进位30,就是01 00000000000(01后跟30个0)  
            public static final int EXACTLY     = 1 << MODE_SHIFT;  
            // 2向左进位30,就是10 00000000000(10后跟30个0)  
            public static final int AT_MOST     = 2 << MODE_SHIFT;  
     
            /** 
             * 根据提供的size和mode得到一个详细的测量结果 
             */  
            // 第一个return:
            // measureSpec = size + mode;   (注意:二进制的加法,不是十进制的加法!)  
            // 这里设计的目的就是使用一个32位的二进制数,32和31位代表了mode的值,后30位代表size的值  
            // 例如size=100(4),mode=AT_MOST,则measureSpec=100+10000...00=10000..00100  
            // 
            // 第二个return:
            // size &; ~MODE_MASK就是取size 的后30位,mode & MODE_MASK就是取mode的前两位,最后执行或运算,得出来的数字,前面2位包含代表mode,后面30位代表size
            public static int makeMeasureSpec(int size, int mode) {  
                if (sUseBrokenMakeMeasureSpec) {
                    return size + mode;
                } else {
                    return (size & ~MODE_MASK) | (mode &  MODE_MASK);
                }
            }  
     
            /** 
             * 获得SpecMode
             */  
            // mode = measureSpec & MODE_MASK;  
            // MODE_MASK = 11 00000000000(11后跟30个0),原理是用MODE_MASK后30位的0替换掉measureSpec后30位中的1,再保留32和31位的mode值。  
            // 例如10 00..00100 & 11 00..00(11后跟30个0) = 10 00..00(AT_MOST),这样就得到了mode的值  
            public static int getMode(int measureSpec) {  
                return (measureSpec & MODE_MASK);  
            }  
     
            /** 
             * 获得SpecSize 
             */  
            // size = measureSpec &  ~MODE_MASK;  
            // 原理同上,不过这次是将MODE_MASK取反,也就是变成了00 111111(00后跟30个1),将32,31替换成0也就是去掉mode,保留后30位的size  
            public static int getSize(int measureSpec) {  
                return (measureSpec &  ~MODE_MASK);  
            }  
    }  

     

    位运算常用的操作符有以下几种:

    1. 或运算符| : 0|0=0,0|1=1,1|1=1
    2. 与运算符& : 0&0=0,0&1=0,1&1=1
    3. 非运算符~ : ~0=1,~1=0
    4. 异或运算符^ : 相同为 0,不同为 1:0^0=0,1^0=1,0^1=1,1^1=0
    5. 右移运算符 >> 和左移运算符 << : 001<<2=100,110>>1=11

    在 MeasureSpec 类中,getMode 方法是将参数 measureSpec 与 MODE_MASK 进行与运算,MODE_MASK 可以理解为 SpecMode 的掩码,运算的结果是保留measureSpec 的高两位,剩下的后 30 位置 0,得到的是 MeasureMode。

    getSize 方法是先将 MODE_MASK 取反再跟 measureSpec 进行与运算,结果是高两位为 0 低 30 位不变的值,即 SpecSize。

    makeMeasureSpec 方法中,size & ~MODE_MASK 的结果是 size 的 SpecSize,mode & MODE_MASK 的结果是 SpecMode,将他们进行或操作,得到的就是是两者的叠加值。

    评论

报告相同问题?

悬赏问题

  • ¥30 STM32 INMP441无法读取数据
  • ¥100 求汇川机器人IRCB300控制器和示教器同版本升级固件文件升级包
  • ¥15 用visualstudio2022创建vue项目后无法启动
  • ¥15 x趋于0时tanx-sinx极限可以拆开算吗
  • ¥500 把面具戴到人脸上,请大家贡献智慧
  • ¥15 任意一个散点图自己下载其js脚本文件并做成独立的案例页面,不要作在线的,要离线状态。
  • ¥15 各位 帮我看看如何写代码,打出来的图形要和如下图呈现的一样,急
  • ¥30 c#打开word开启修订并实时显示批注
  • ¥15 如何解决ldsc的这条报错/index error
  • ¥15 VS2022+WDK驱动开发环境