众所周知,Android的自定义View有三大方法,分别为测量、布局、绘制。
在两年多之前,我进行过绘制过程的大致梳理,然而就没有在研究过这个流程了。然后,直到有一天,一次面试过程中,被面试官问道:onMeasure(int widthMeasureSpec, int heightMeasureSpec)里,这个两个值应该如何去看?
我恍然大悟,我一直漏掉这一块(之前的Android自定义布局,这个方法几乎没有用到过)。尝试去了解,理解得云里雾里的。最近在博智林开发与机器人相关的App,于是乎,对于 | 与 & 这两个概念,以及负数在二进制中如何表示有了更深的理解。原来才知道,这个测量事件的两个值,是这样的理解。
Android的自定义View onMeasure方法,会带着两个参数,分别是widthMeasureSpec与heightMeasureSpec。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
那么,这个两个参数到底是什么含义呢?有人说是元数据,我回过头来看,确实理解了元数据这个概念,但是,如果你还没有理解,可以先忽略这个概念。
我把上面的方法,换一个写法,也可以正常的使用。
@Override
protected void onMeasure(int widthModel,int widthSize, int heightModel,int heightSize) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
我把widthMeasureSpec 分解成 int widthModel,int widthSize,把heightMeasureSpec分解成 int heightModel,int heightSize。
其中 widthModel 代表模式,也就是我们在xml文件中写的那些wrap_content,match_parent。而widthSize代表具体的大小。
这样一看,就很明白了。但是真实的场景,是一个参数代表两个意思。那么是如何做到的呢?
int 类型,在计算机里是32位的。一般的参数,一般不会全部用满32位,那么没有用满的数据,就用0表示。所以, 我们这里把widthMeasureSpec 看成是一个32位的数据,每一位上,可以是0,也可以是1。
Android把32位的从左到右的前两位,约定代表模式,也就是上述的 widthModel ,把32位的从左到右的后30位,约定代表大小,也就是上述的widthSize。还记得上面提到的元数据的概念吗?元数据就是还未分解的数据。
那么,如何分解?Android提供了一个类MeasureSpec,供我们使用。
获得模式的方法:
public static int getMode(int measureSpec) {
//noinspection ResourceType
return (measureSpec & MODE_MASK);
}
获得大小的方法:
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
上述两个方法,都用到一个MODE_MASK的变量,这个变量是什么呢?
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
这个变量是0x3 向左移动 30位 的值。0x3的二进制是11,向左移动30位,变成一个11开头后面有30个0的数字。
这里再明确两个概念。
第一个概念:&。数学里的与。举个例子当1与0时,等于0。当1与1时,等于1。1在与任何数时,等于任何数。也可以说,0与任何数,都成0。&,是二进制层面的。
第二个概念:~。数学里的取反。举个例子0x3的二进制是11,向左移动30位,变成一个11开头后面有30个0的数字。然后再取反,就变成了00开头后面有30个1的数字。取反就是每一位上,0会变成1,1会变成0。
明确了上述概念后,我们再来看那两个方法。
获得模式的方法:
public static int getMode(int measureSpec) {
//noinspection ResourceType
return (measureSpec & MODE_MASK);
}
首先,传进来一个元数据,与上MODE_MASK,而MODE_MASK上面我们已经说了,是一个开头是11后面跟着30个0的数字。在加上上面的第一个概念,其实这里是使元数据的后30位变成0。去掉后30位的数据,代表的就是模式。
获得大小的方法:
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
这个方法,得到模式的方法,相差的是一个~的符号。根据上述的第二个概念,measureSpec与的数字是 前面00后面带30个1的数字。所以这里的操作其实就是在去掉前2位的数据,而去掉前两位的数据后,代表的就是大小。
来源:CSDN
作者:远方的列车
链接:https://blog.csdn.net/s2311307/article/details/103973602