<自叙>
对于初学者来说,想搞懂android中view出现在屏幕上三个阶段(测量[onMeasure],布局[onLayout]和绘制[onDraw])是很困难的,我也是一名android初学者,这些东西老师教不了我们,如果他们知道教给我们,也只能靠我们自己上网找资料,自己理解。
对于view的绘制我也是半懂不懂,网上看了很多,或许你们和我一样不能完全参透,或许只能耐着性子看几万行源码了,在这里,我只讲我理解的部分共享给你们,希望你们对于这些知识有更深的理解。
想必对于一些曾探寻过这些知识的人对android的MeasureSpec类并不陌生,但是就是不知道MeasureSpec类追根到底是怎么样做到把模式和尺寸集于一体的,然后又是怎么样很方便的获取的他们各自的值。
<讲解>
我们都知道MeasureSpec是view的内部类,是一个很简单的类,里面仅有几十行代码,五个成员变量和四个成员方法,下面是MeasureSpec类的源码:
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
public static String toString(int measureSpec) {
}
<讲变量>
我们先从成员变量说起,它的五个成员变量分别为: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.第一个方法是:
这个方法很简单,直接将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 ,EXACTLY,AT_MOST中的某一个),得到一个尺寸size,是不是就将mode和size在测量值中取出来了。接下来,我们看看在下面的两个方法中究竟是怎么运算取出他们的。
2.第二个方法是:
又是一个很简单的方法,一句话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.第三个方法是:
想必这个方法不用讲,通过第二个方法的讲解大家就已经知道了吧,可是为了曾经
看别人讲解看不懂得到经验的我,还是讲一下吧。
首先MODE_MASK做非运算得到二进制:0011 1111 1111 1111 1111 1111 1111 111
借用前面的一句话,任何数跟1相与都得任何数,任何数跟0相与都得0,所以结果值的前两位
即第三十一位和第三十二位都是0,其余位是measureSpec除前两位的其余位,而它的这些位上正好
储存了size值,也就取出了它的尺寸size。
写到此,toString方法就不做讲解了,这个方法主要用来打印,里面的逻辑代码没必要去纠结,
所以,整个类MeasureSpec到此完结,看懂的朋友一定要评论一下哦,嘻嘻。欢迎大家对我
哪里讲的不好的做出点评,谢谢大家的浏览,我会继续努力,自己不懂的绝不会拿上来讲。