一 什么是MeasureSpec
我们在上篇文章中已经了解到了UI绘制最终会走到ViewRootImpl的performTraversals()这个方法:
private void performTraversals() {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
performLayout(lp, mWidth, mHeight);
performDraw();
}
可以看到,在执行测量的时候,首先会生成childWidthMeasureSpec ,childHeightMeasureSpec 这两个参数,这其实就是DecorView的测量规则,这里解释一下,所谓测量规则,其实就是父View对子View的一种限制,在测量子View的宽度和高度的时候,不能只看我们设置它的本身的大小,还要综合父View给的测量规则综合的得到子View在屏幕上的具体大小;我们知道android的界面顶层的View其实就是DecorView,而我们在加载界面的时候,自然要先得到它的测量规则才能继续测量 我们来看一下getRootMeasureSpec()
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
这里会根据DecorView的宽高模式来得到具体的MeasureSpec,最终返回一个int类型的值;其实MeasureSpec类似一个工具类,会根据传入的具体参数得到一个int数据类型,里面封装了测量模式和具体的size,接下来我们具体来看一下MeasureSpec这个类。
二 MeasureSpec的源码解析
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
@IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
@Retention(RetentionPolicy.SOURCE)
public @interface MeasureSpecMode {}
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(@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);
}
}
MeasureSpec定义了三个变量,代表的是测量模式,就是父View对子View的一个限制
不确定的,这个在Android开发中很少用
精确值,子View的大小可以是具体的数值,也可以是match_parent,因为在这个精确模式下,父view的大小是确定的,所以子view直接是等于父view的代销
子view可以是任意值,但是前提是不能超过父view的大小,对应的子view的大小模式是wrap_parent,所以这个模式也叫最大值模式
可以看到,这三个值都通过了位运算,将对应的二进制位左移到了最左端(int数据类型是32位,也就是32和31位);接着我们来看一下makeMeasureSpec()这个方法,这个方法本质上就是将我们上面说的测量模式和传入的父view大小封装到一个int数据类型的32位二进制中,具体的运算上面写得很清楚了。
OK,现在通过了makeMeasureSpec()这个方法,我们将传入的DecorView的大小和模式封装成一个32位的int数据类型,那么我们又要如何获取size或者测量模式呢,MeasureSpec也提供了两个方法给我们,分别是getMode()和 getSize()
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
很显然,将测量规则和MODE_MASK进行与运算,就得到了测量模式,跟~MODE_MASK进行与运算就得到了大小
|