MeasureSpec
MeasureSpec的全称是Measure Specification,意为“测量规格”。一个MeasureSpec对象封装了父布局传递给子布局的布局要求,每个MeasureSpec对象代表了一组宽度和高度要求。一个MeasureSpec对象由size和mode组成,MeasureSpec类通过将其封装在一个int值中以减少对象的分配。MeasureSpec的模式有三种:
UNSPECIFIED父布局不对子元素做任何的约束,子元素可以得到任何想要的大小。
EXACTLY子元素会以一个确定的值作为大小。
AT_MOST子元素最多能是指定的大小。
Constantsprivate static final int MODE_SHIFT该值指定了MeasureSpec三种模式切换时的进位大小,其默认值为30,意思是进位的大小为2的30次方。那么为什么会是30呢?我们知道一个int的长度大小为32位,而进位30位意思就是使用int的最高位和倒数第二位也就是32和31位作为标识位。
private static final int MODE_MASK该值指定了MeasureSpec在进行位运算时的遮罩常量,其默认值为0x3 << MODE_SHIFT,也就是十六进制下的0x3左移30(MODE_SHIFT)位。十六进制下的0x3换位二进制即为11,左移30位即为:1100 0000 0000 0000 0000 0000 0000 0000。那这个遮罩的作用是什么?为什么要叫遮罩呢?其实其作用非常简单,只是用1标识需要的值而用0标识不需要的值,因为1与任何数做与运算都会得到该数而0与任何数做与运算都会得到0,就这么简单,这也就是为什么称其为遮罩,因为其能通过运算将不需要的值位“隐藏”起来而将需要的值位“显示”出来。
public static final int UNSPECIFIED该值的意义开头有讲,这里就不重复累赘了。其默认值为0 << MODE_SHIFT,也就是十进制下的0左移30(MODE_SHIFT)位。十进制下的0换位二进制依然为0,左移30位也就是31个0,这里我们为了规范,将在左端补0凑够32位:0000 0000 0000 0000 0000 0000 0000 0000。
public static final int EXACTLY该默认值为1 << MODE_SHIFT,也就是十进制下的1左移30(MODE_SHIFT)位。十进制下的1换位二进制依然为1,左移30位也就是1后跟30个0,这里我们为了规范,将在左端补0凑够32位:0100 0000 0000 0000 0000 0000 0000 0000。
public static final int AT_MOST该默认值为2 << MODE_SHIFT,也就是十进制下的2左移30(MODE_SHIFT)位。十进制下的2换位二进制为10,左移30位也就是10后跟30个0:1000 0000 0000 0000 0000 0000 0000 0000。
MeasureSpec的三种模式就介绍到这里,大家有木有发现?这三种模式包括MODE_MASK的值在二进制下都只占两位,移位后刚好在最高位和倒数第二位也就是32和31位上!有木有感触?结合上面对MODE_MASK的介绍自己脑补下,下面我们来看看MeasureSpec类的方法
Methods在介绍MeasureSpec类的方法之前我先要在这介绍一下View类的onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,该方法有两个int型的参数值widthMeasureSpec和heightMeasureSpec,这两个值我们输出一下看看:
可以看到我们输出了两次相同的结果,也就是说onMeasure方法重复输出了两次,事实上该方法有可能会执行多次来对View进行测量,这具体得看View类的实现,我在讲View类的时候再细说,这里我们获取到onMeasure方法的两个参数值:
widthMeasureSpec = 1073742144(十进制) = 0100 0000 0000 0000 0000 0001 0100 0000(二进制)
heightMeasureSpec= 1073742260(十进制) = 0100 0000 0000 0000 0000 0001 1011 0100(二进制)
上面我们说到,MeasureSpec类用一个int值存储了size和mode两个属性,也就是说上面两个参数值均包含了size和mode两个属性在里面,那么系统又是如何提取和封装它们的呢?这里我先将其转换为二进制并补全其至32位便于我们计算,下面来看MeasureSpec的方法。
public static int getSize(int measureSpec)顾名思义,该方法返回了MeasureSpec的size值,其具体实现如下:
[java] view plaincopyprint?
- return (measureSpec & ~MODE_MASK);
getSize的输出结果为320(十进制) = 0000 0000 0000 0000 0000 0001 0100 0000(二进制),那我们是如何得到这个值的呢?首先,取反MODE_MASK:
~MODE_MASK = ~1100 0000 0000 0000 0000 0000 0000 0000 = 0011 1111 1111 1111 1111 1111 1111 1111
再者,按位与:
0100 0000 0000 0000 0000 0001 0100 0000(widthMeasureSpec)
&
0011 1111 1111 1111 1111 1111 1111 1111(~MODE_MASK )
------------------------------------------------------------
0000 0000 0000 0000 0000 0001 0100 0000
转为十进制即为320,大家发现没有?Android巧妙地用Mask将size值从低位“提取”了出来!getSzie方法的原理就很清晰了:MODE_MASK取反后将32,31替换成0也就是去掉mode保留后30位的size。heightMeasureSpec的值也是一样的方法提取,下面我们再来看看与getSize类似的另一个方法
public static int getMode(int measureSpec)该方法与getSize类似,是从MeasureSpec值中获取mode值,这里我在布局文件中设置该自定义控件的layout_width为match_parent:
[html] view plaincopyprint?
- <com.aigestudio.test.MyView
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
可以得到mode的值为1073741824(十进制) = 0100 0000 0000 0000 0000 0000 0000 0000(二进制),与上面的三类模式对比不难发现与EXACTLY的值一样,我们来看看该方法的具体实现:
[java] view plaincopyprint?
- return (measureSpec & MODE_MASK)
0100 0000 0000 0000 0000 0001 0100 0000(widthMeasureSpec)
&
1100 0000 0000 0000 0000 0000 0000 0000(MODE_MASK )
------------------------------------------------------------
0100 0000 0000 0000 0000 0000 0000 0000
看到木有!看到这里大家明白这个MODE_MASK的作用了么?将mode存储在32和31位中而将size存储在后30位中!这也是为什么MODE_SHIFT为30的原因!不难理解吧……
举一反三,下面我们将MyView的layout_width改为wrap_content看看:
[html] view plaincopyprint?
- <com.aigestudio.test.MyView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
得到mode的值为-2147483648(十进制) = 1000 0000 0000 0000 0000 0000 0000 0000(二进制),与上面的三类模式对比不难发现与AT_MOST的值一样,具体计算过程我就不写了。getMode方法的原理与上面的getSzie方法相反:用MODE_MASK后30位的0替换掉measureSpec后30位中的1再保留32和31位的mode值。
既然可以从MeasureSpec值中“提取”size和mode那必然也有对应的方法将其“封装”至MeasureSpec值中,下面我们就来看看这个方法
public static int makeMeasureSpec(int size, int mode)[java] view plaincopyprint?
- if (sUseBrokenMakeMeasureSpec) {
- return size + mode;
- } else {
- return (size & ~MODE_MASK) | (mode & MODE_MASK);
- }
[java] view plaincopyprint?
- public View(Context context) {
- //……此处省略无关代码
- if (!sCompatibilityDone && context != null) {
- final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
- sUseBrokenMakeMeasureSpec = targetSdkVersion <= JELLY_BEAN_MR1;
- //……此处省略无关代码
- }
- //……此处省略无关代码
- }
static int adjust(int measureSpec, int delta)该方法是一个默认修饰符方法,仅供android.view包调用,View中不会直接调用上面的makeMeasureSpec方法但是会通过该方法的封装来间接地在measure方法中调用makeMeasureSpec方法,该方法会对View的布局长宽作调整,具体我在将View类的measure方法再说,其实现如下,其实看看计算过程你也能略知一二:
[java] view plaincopyprint?
- return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec));
[java] view plaincopyprint?
- 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);
Tag:
Freenovo 发表于2014-08-24 21:07:00 | 编辑 | 分享 0
引用地址: