上篇我们讲了MeasureSpec类如何通过bit-mask高效地储存mode和size
本篇我们来说说到底什么是MeasureSpec.EXACT,什么又是WRAP_CONTENT,它们的关系又是如何。
ViewGroup.LayoutParams类
在XML中看到的WRAP_CONTENT等实际属于LayoutParams类,这个类是FrameLayout的内部静态类,父类是ViewGroup.MarginLayoutParams,而他的父类是ViewGroup.LayoutParams, 所以
FrameLayout.LayoutParams extends ViewGroup.MarginLayoutParams extends ViewGroup.LayoutParams
先看最顶端的父类ViewGroup.LayoutParams,他有三个常量,三个变量
public static final int FILL_PARENT = -1 // 旧版的MATCH_PARENT
public static final int MATCH_PARENT = -1 // 大小等于parent view - padding
public static final int WRAP_CONTENT = -2 // 和内容大小 + 自己的padding
public int width // 确切的宽度
public int height // 确切的高度
public LayoutAnimationController.AnimationParameters layoutAnimationParameters; // 用来动画的,这个和onMeasure无关,先不看
四个constructor
public LayoutParams(Context c, AttributeSet attrs) // 读取xml
public LayoutParams(int width, int height) // 直接给长宽
public LayoutParams(LayoutParams source) // 复制已有的layoutParam
LayoutParams() // 空白函数,被MarginLayout的constructor调用了一次,不明白为什么要这个
一个设置长宽的便捷函数
protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) // MarginLayout会调用这个函数,确保设置长宽
一个设置方向的函数
public void resolveLayoutDirection(int layoutDirection) // 也是被MarginLayout调用的
两个debug函数,一个方便打印的函数,就不列出了。
可以看到这个最基础的类实际没有做太多工作,只是基本结构给建好了。
ViewGroup.MarginLayoutParams类
这个类集中对margin进行了处理。这个类比较复杂,值得专门开一个章节去讲,onMeasure和它关系不大,这里就不仔细讲了。
FrameLayout.MarginLayoutParams类
这个类在处理了margin(MarginLayoutParams)的基础上,加入了gravity。gravity实际是所有child view对齐的方向。这个类只有几个不同的constructor,分别是:
public LayoutParams(Context c, AttributeSet attrs) // 从xml读取属性,相比ViewGroup.LayoutParams的对应构建函数,加入了读取gravity的部分
public LayoutParams(int width, int height) // 直接设置长宽, gravity 默认是 -1
public LayoutParams(int width, int height, int gravity) // 直接设置长宽和重力
public LayoutParams(ViewGroup.LayoutParams source) // 复制已有ViewGroup.LayoutParams
public LayoutParams(ViewGroup.MarginLayoutParams source)// 复制已有MarginLayoutParams
public LayoutParams(LayoutParams source) // 复制已有FrameLayout.LayoutParams
因为FrameLayout.LayoutParams是继承ViewGroup.LayoutParams所以也有WRAP_CONTENT等成员常/变量。具体而言有三种情况
FILL_PARENT/ MATCH_PARENT
WRAP_CONTENT
具体width/height
所以width和height如果是-1或者-2就是前两种,如果是>=0的值则是第三种。
而MeasureSpec类的三种情况则是
EXACTLY
AT_MOST
UNSPECIFIED
接下来我们看看这6个变量之间的关系。
常/变量的含义及关系
我们继续阅读FrameLayout.java的onMeasure方法,会看到它对每个child view都会调用measureChildWithMargins方法来测量大小。
measureChildWithMargins
我们来看这个方法最后实际是调用了child的measure方法。child的measure方法实际是把已经测量好的childWidthMeasureSpec和childHeightMeasureSpec做了cache, 然后调用了child的onMeasure方法。
1. 实际测量的过程在getChildMeasureSpec这个方法。
2. 注意传入参数,2为父类的measureSpec
3. 为各种padding和margin的和
4. 为lp.width,即child view的宽度值,可能是WRAP_CONTENT, MATCH_PARENT或是具体值
getChildMeasureSpec
再来看getChildMeasureSpec方法的第一部分, 注意,spec是parent的measureSpec, childDimension是child的layoutParam类型(WRAP_CONTENT/MATCH_PARENT/具体值)
1. 把父类的mode和size拿出来
2. 把parent view的大小减去child view的padding和margin,要求>=0
接下来判断resultSize和resultMode的值。resultSize和resultMode会组成child的measureSpec。
首先判断parent的specMode,然后判断child的layoutParam类型,由此决定child 的measureSpec。这样两者的关系,大致清楚了。
比如:
1. 如果parent measureSpec的Mode是EXACTLY
2. 如果child的layoutParam类型是具体数值
3. 那么child的MeasureSpec中size = layoutParam的size, mode等于EXACTLY
总结一下
关系
MeasureSpec是parent对child的要求,layoutParam是child对自己的愿望,child的MeasureSpec(child 对自己的child的要求)由parent的要求和child的愿望决定。
含义
(A是parent, B是child,第三句是结果,注意parent的大小是减去了padding和margin的)
第一组
A"给我具体大小",B”我要具体大小“,B的具体大小
A“给我具体大小”,B"我要和你一样“,和A一样的大
A"给我具体大小",B"最多和你一样",最多和A一样大
第二组
A"最多和我一样大",B”我要具体大小“,B的具体大小
A"最多和我一样大",B"我要和你一样",最多和A一样大
A"最多和我一样大",B"最多和你一样",最多和A一样大
第三组
A"不知道啊",B”我要具体大小“,B的具体大小
A"不知道啊",B"我要和你一样“,不知道啊先等于0吧
A"不知道啊",B"最多和你一样",不知道啊先等于0吧
用法