<android>MeasureSpec的彻底讲解

     <自叙>

     对于初学者来说,想搞懂android中view出现在屏幕上三个阶段(测量[onMeasure],布局[onLayout]和绘制[onDraw])是很困难的,我也是一名android初学者,这些东西老师教不了我们,如果他们知道教给我们,也只能靠我们自己上网找资料,自己理解。


     对于view的绘制我也是半懂不懂,网上看了很多,或许你们和我一样不能完全参透,或许只能耐着性子看几万行源码了,在这里,我只讲我理解的部分共享给你们,希望你们对于这些知识有更深的理解。


     想必对于一些曾探寻过这些知识的人对android的MeasureSpec类并不陌生,但是就是不知道MeasureSpec类追根到底是怎么样做到把模式和尺寸集于一体的,然后又是怎么样很方便的获取的他们各自的值。


       <讲解>

      我们都知道MeasureSpec是view的内部类,是一个很简单的类,里面仅有几十行代码,五个成员变量和四个成员方法,下面是MeasureSpec类的源码:



public static class MeasureSpec {

        private static final int MODE_SHIFT = 30;


        private static final int  MODE_MASK   =  0x3 << MODE_SHIFT ;

        public static final int UNSPECIFIED = 0 << MODE_SHIFT;

        public static final int EXACTLY     = 1 << MODE_SHIFT;

        public static final int AT_MOST     = 2 << MODE_SHIFT;


        public static int makeMeasureSpec(int size, int mode) {
            return size + mode;
        }

             //这是得到控件的模式(说直白一点就是wrap_content, match_parent......)
            public static int getMode(int measureSpec) {
            return ( measureSpec &   MODE_MASK );
              }

//这是计算控件的大小
        public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }


               //这是打印字符串

        public static String toString(int measureSpec) {


            int mode = getMode(measureSpec);
                 int size = getSize(measureSpec);


            StringBuilder sb = new StringBuilder("MeasureSpec: ");


            if (mode == UNSPECIFIED)
                sb.append("UNSPECIFIED ");
            else if (mode == EXACTLY)
                 sb.append("EXACTLY ");
            else if (mode == AT_MOST)
                sb.append("AT_MOST ");
            else
                sb.append(mode).append(" ");


            sb.append(size);
            return sb.toString();
        }

    }

     <讲变量>

     我们先从成员变量说起,它的五个成员变量分别为:MODE_SHIFT ,  MODE_MASK UNSPECIFIED ,  EXACTLY,   AT_MOST


     他们的值分别为:30, -1073741824和0, 1073741824,-2147483648


再看看他们的二进制表示:


MODE_SHIFT: 

0000 0000 0000 0000 0000 0000 0001 1110 (30)


MODE_MASK: 

1100 0000 0000 0000 0000 0000 0000 0000( -1073741824)


UNSPECIFIED:

0000 0000 0000 0000 0000 0000 0000 0000(0)


EXACTLY: 

0100 0000 0000 0000 0000 0000 0000 0000(1073741824)


 AT_MOST: 

1000 0000 0000 0000 0000 0000 0000 0000(-2147483648)


      看到这里我相信应该有很多人已经明白了,对于他们的二进制表示是很有安排性的,这对于后面方法中的与和非的运算操作埋下了伏笔。

      <讲方法>

      让我再逐一从它的方法来讲解:


      1.第一个方法是:

public static int  makeMeasureSpec (int  size , int  mode ) {
           return  size + mode;
        }

       这个方法很简单,直接将size和mode相加,这个方法的作用是制作一个测量值。一个测量值能干什么?

其实一个测量值中存储了size和mode,所以我们可以通过makeMeasureSpec将自己的size和mode保存起来,保存起来的目的就是让我们可以再次通过MeasureSpec类取出他们,以达到我们的业务逻辑的实现。因为他们都是int类型,最高两位也就是第三十一位和第三十二位代表模式mode,而其余位是代表大小size,也就是说size+mode将会把第三十一位,第三十二位的值以及其余位的值统统结合在一起,以便再在其他方法中通过相应的逻辑运算取出他们,再看看我的详细讲解:

        <详细讲解>

         其实mode不一定是正数,而size一定是正数,所以size+mode有可能是size-|mode|,但是有一点我们可以知道,size的最高两位永远是0,而第三十二位是符号位,符号位可以排除在运算范围之外(讲到这里,如果大家对这些有困惑,可以看看我的第一篇文章《原码,补码和反码...》),接下来无非mode的值有三种情况,第一种情况:最高两位都是0,第二种情况:第三十二位是0,第三十一位是1,而这两种情况下,size和mode是两个正数,我们就用二进制进行加法运算(和十进制的加法运算几乎没有任何区别,要说区别也只是一个是满十进一,一个是满二进一),我们知道任何数和0相加永远是任何数,因为size的最高两位永远是0,所以size+mode的结果值的最高两位就是mode的最高两位,即将mode保存在了测量值之中。而mode的其余位全是0,所以size+mode的其余位也就是size的其余位,即将size保存在了测量值之中;mode还有第三种情况:第三十二位是1,第三十一位是0,这时候mode其实是个负数,所以size+mode=size-|mode|,但是第三种情况下size的最高两位肯定是两个0,mode的第三十二位是1,第三十一位是0,其余位上全部是0。而任何数减0都是任何数,所以size-|mode|的最高两位是10(再次强调符号位不参与运算),即将mode保存下来了,其余位上的值也就是size其余位上的值,即将size保存下来了。<其实size+mode完全可以用size | mode代替>而这种逻辑运算一定要用到一个桥梁变量:MODE_MASK


        讲到这里我就要先说一说他们中间的桥梁变量MODE_MASK,这个变量到底是干什么的呢?

我们看看MODE_MASK的二进制:1100 0000 0000 0000 0000 0000 0000 0000

最高位是两个1,其余全部是0,那么我们就可以猜想,如果一个特别的数与它相与会得到一个什么样的期望结果?刚才我们是不是可以通过makeMeasureSpec制作一个测量值,那么接下来测量值与MODE_MASK之间的与运算就是我们期望的美好结果,即得到一个模式mode(必然是UNSPECIFIED ,EXACTLYAT_MOST中的某一个),得到一个尺寸size,是不是就将mode和size在测量值中取出来了。接下来,我们看看在下面的两个方法中究竟是怎么运算取出他们的。


        2.第二个方法是:

  public static int  getMode (int  measureSpec ) {
           return ( measureSpec & MODE_MASK);
        }

       又是一个很简单的方法,一句话measureSpec & MODE_MASK就能取出

测量值mesureSpec中的mode我们知道任何数跟1相与都得任何数,任何数跟

0相与都得0,而MODE_MASK的二进制是:1100 0000 0000 0000 0000 0000 

0000 0000,由此我们可以确定这个结果值后面30位都是0,measureSpec第

三十一位和第三十二位是多少,那么这个结果值的第三十一位和第三十二位也

就是多少,也就将measureSpec的mode取出来了其结果有三种情况:


结果一:0000 0000 0000 0000 0000 0000 0000即成员变量:UNSPECIFIED 

结果二:0100 0000 0000 0000 0000 0000 0000即成员变量:EXACTLY 

结果三:1000 0000 0000 0000 0000 0000 0000即成员变量:AT_MOST 


注意:不存在1100 0000 0000 0000 0000 0000 0000

除非你自己传一个最高位是两个1的int值,

android内部会做处理,究竟怎么处理,

我想应该会选取EXACTLY AT_MOST中的某一个


        3.第三个方法是:

        public static int  getSize (int  measureSpec ) {
            return ( measureSpec & ~MODE_MASK);
        }


       想必这个方法不用讲,通过第二个方法的讲解大家就已经知道了吧,可是为了曾经

看别人讲解看不懂得到经验的我,还是讲一下吧。

       首先MODE_MASK做非运算得到二进制:0011 1111 1111 1111 1111 1111 1111 111

       借用前面的一句话,任何数跟1相与都得任何数,任何数跟0相与都得0,所以结果值的前两位

即第三十一位和第三十二位都是0,其余位是measureSpec除前两位的其余位,而它的这些位上正好

储存了size值,也就取出了它的尺寸size。


写到此,toString方法就不做讲解了,这个方法主要用来打印,里面的逻辑代码没必要去纠结,

所以,整个类MeasureSpec到此完结,看懂的朋友一定要评论一下哦,嘻嘻。欢迎大家对我

哪里讲的不好的做出点评,谢谢大家的浏览,我会继续努力,自己不懂的绝不会拿上来讲。

     

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值