前面找到了 View 工作流程的入口,这次来再来看看 measure 部分。
MeasureSpec
要了解 measure 过程,这个概念是避不开的。MeasureSpec 代表着父 View 传递过来的对布局尺寸的要求,它是一个大小和样式的组合值。
- UNSPECIFIED 对子 View 没有任何要求
- EXACTLY 要求子 View 明确的尺寸,不管子 View 想要多大
- AT_MOST 给与子 View 空间,使得子 View 的尺寸可以尽可能的接近某个尺寸
MeasureSpec 用一个 int 值来表达大小和样式,这样就避免了内存空间的申请和消耗,一个 int 有 32 位,高 2 位用来表示上面的三种样式,低 30 位就用来表示尺寸了,代码里是通过左移 30 位实现的。
UNSPECIFIED 对应的高两位就是 00,EXACTLY 对应的是 01,AT_MOST 对应的是 10。
//简单理解就是将 size 和 mode 合并成一个新值
public static int makeMeasureSpec(int size, int mode) {
//用来判断是用老逻辑还是新逻辑计算
if(sUseBrokenMakeMeasureSpec) {
//直接求和的方式
return size + mode;
} else {
//通过位运算,前面括号确保 size 的干净,就是将 size 的高两位置为 0
//后面括号确保 mode 的干净,就是将 mode 的低 30 位置为 0
//最后进行按位或
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
//简单理解就是根据 mode,对尺寸进行 delta 值的调整
static int adjust(int measureSpec, int delta) {
final int mode = getMode(measureSpec);
int size = getSize(measureSpec);
//这种 mode 很自由,管不着,所以对它是无效的
if (mode == UNSPECIFIED) {
return makeMeasureSpec(size, UNSPECIFIED);
}
//对尺寸进行 delta 值的调整
size += delta;
if (size < 0) {
size = 0;
}
//将调整后的 mode + size 值返回
return makeMeasureSpec(size, mode);
}
onMeasure
measure 会从根视图 DecorView 开始,在 ViewRootImpl 里会触发 performMeasure() 方法,
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
//这个 mView 就是 DecorView 对象
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
//View 的 measure
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
//这段在我的理解是,看看 View 的边界是否透明,如果和父 View 的透明样式不一致,那要做尺寸的调整
//对 decorview 的 mParent 还没验证过是什么值
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int oWidth = insets.left + insets.right;
int oHeight = insets.top + insets.bottom;
widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth);
heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
}
//再往下的一段用来判断 MeasureSpec 较上次是否变化,或者有没有必要去做布局调整,因为一般只有变换了才去做布局调整。
if (forceLayout || needsLayout) {
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
//需要调整的时候,不管走下面哪个条件,都会重新赋值 测量宽度和测量高度
if (cacheIndex < 0 || sIgnoreMeasureCache) {
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
long value = mMeasureCache.valueAt(cacheIndex);
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
}
//更新 MeasureSpec
mOldWidthMeasureSpec = widthMeasureSpec;
mOldHeightMeasureSpec = heightMeasureSpec;
//看样子是将测量高度和测量宽度做了缓存,
//结合前面的和测量宽高度相关的判断,意思就是如果没有缓存就执行 onMeasure
//如果有缓存就取缓存赋值
mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 | (long) mMeasuredHeight & 0xffffffffL);
}
这么来看,View 的 measure() 方法根据入参只负责自己的尺寸测量,但是当前考虑的是根视图 DecorView,它首先继承自 FrameLayout,FrameLayout 又继承自 ViewGroup,ViewGroup 又继承自 View。而 FrameLayout 和 DecorView 都对 onMeasure() 方法进行了重写。
所以会先执行 DecorView 的 onMeasure() 方法,再执行 FrameLayout 的 onMeasure() 方法,而前者侧重对入参 widthMeasureSpec,heightMeasureSpec 的计算,后者则是更注重对子 View 的遍历以及让各子 View 结合 margin 和 padding 等进行测量。
同理,再往大了推,类似 FrameLayout 的 ViewGroup 子类,比如 LinearLayout, RelativeLayout 这种应该都有自己的 onMeasure 实现。