MeasureSpec
在初学自定义View的时候有了解过,大致清楚作为onMeasure方法的两个参数,里面包含了父View分配的测量模式和可用size,但是具体的细节不太清楚。现在趁着看measure相关的源码的机会干脆理一理MeasureSpec
。
MeasureSpec
是定义在View这个类中的内部静态类,方法不多,主要提供了测量相关参数解析和构建的方法。
MeasureSpec中的变量
MODE_SHIFT
:值30,用于位移运算MODE_MASK
:值是3向左位移MODE_SHIFT位,用于计算测量模式的UNSPECIFIED
:值是0向左位移MODE_SHIFT位EXACTLY
:值是1向左位移MODE_SHIFT位AT_MOST
:值是2向左位移MODE_SHIFT位
从这些变量可以看出一个int值存储测量的相关信息,这个值的高两位用来存储测量模式,剩下的30位用于存储size。
MeasureSpec中的方法
makeMeasureSpec(int,int)
public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
@MeasureSpecMode int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
它有两个参数,一个size一个测量模式。可以看到这个方法通过变量sUseBrokenMakeMeasureSpec
选择两种实现方式。而sUseBrokenMakeMeasureSpec
由Android api等级决定。
// Older apps may need this compatibility hack for measurement.
sUseBrokenMakeMeasureSpec = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
-
size + mode
在高两位和后面的尺寸互不干扰的情况下能够得出正确的MeasureSpec,这说明还有异常情况,从方法注释中可以了解到在Android api 17以前就是只使用了这种实现方式会产生bug。这就产生了第二种计算方式。
//Android api 17的源码,可以看到没有做异常值的处理 public static int makeMeasureSpec(int size, int mode) { return size + mode; }
-
(size & ~MODE_MASK) | (mode & MODE_MASK)
(size & ~MODE_MASK)
保留了size后30位的数据,抹掉了高两位的数据。(mode & MODE_MASK)
以同样的方式保留了高两位的测量模式。最后通过|
运算将两个数据合并(个人认为这里用+
运算问题也不大,因为溢出的数据已经通过&
运算处理了)。这里个人其实是有个疑问,既然这个方法的参数上都添加注解来限制输入了,为什么还要做这些处理,仔细看了方法的注释文档后看到这句makeMeasureSpec's implementation was such that the order of arguments did not matter
意识到可能最开始并没有注解。//这是api 18的源码,可见并没注解,直到api 24才在参数的位置加上注解。 public static int makeMeasureSpec ( int size, int mode){ if (sUseBrokenMakeMeasureSpec) { return size + mode; } else { return (size & ~MODE_MASK) | (mode & MODE_MASK); } }
makeSafeMeasureSpec(int,int)
仅供内部使用,以与系统小部件和较旧的应用程序兼容。
getSize(int)
(measureSpec & ~MODE_MASK)
,位运算直接取出size
getMode(int)
(measureSpec & MODE_MASK)
,位运算取出高两位的测量模式
adjust(int, int)
这个方法只在View的measure方法中,测量光学边界的时候用到。具体逻辑是当测量模式不是UNSPECIFIED的时候讲size扩充delta的大小。但是目前还不清楚测量光学边界的作用是什么,个人感觉并不是特别重要的内容,所以安排以后再去了解。