总结来说 mode 取值 受父容器与子控件共同决定
在自定义控件时为了满足特定需求,widget大都是我们自己测量的。
大家都知道测量时最重要的步骤就是重写onMeasure方法,来计算出宽高。
这里面的MeasureSpec 很重要,大家也都知道,它是一个java中的静态类,它有重要的三个静态常量和三个最重要的静态方法。
我这里说下MeasureSpec 的3中模式
UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;
EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
AT_MOST(至多),子元素至多达到指定大小的值。
看过很多博客,大家总结说,模式和XML布局有如下对应关系:
wrap_content-> MeasureSpec.AT_MOST
match_parent -> MeasureSpec.EXACTLY
50dp(确切值) -> MeasureSpec.EXACTLY
但是在具体开发中 我发现并不是一定这样,然后就看了下源码,才发现子控件的mode不只是与自身的设置有关系,它还受到父容器影响。
getChildMeasureSpec() 方法
- public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
- int specMode = MeasureSpec.getMode(spec);
- int specSize = MeasureSpec.getSize(spec);
- int size = Math.max(0, specSize - padding);
- int resultSize = 0;
- int resultMode = 0;
- switch (specMode) {
- // Parent has imposed an exact size on us
- // 父容器是 EXACTLY 模式
- case MeasureSpec.EXACTLY:
- if (childDimension >= 0) {
- // 如果 child 希望有自己的尺寸,那么允许它,并把它的测量模式设置为 EXACTLY
- resultSize = childDimension;
- resultMode = MeasureSpec.EXACTLY;
- } else if (childDimension == LayoutParams.MATCH_PARENT) {
- // Child wants to be our size. So be it.
- // child 希望尺寸与 父容器 一样大,那么允许它
- resultSize = size;
- resultMode = MeasureSpec.EXACTLY;
- } else if (childDimension == LayoutParams.WRAP_CONTENT) {
- // Child wants to determine its own size. It can't be
- // bigger than us.
- // child 希望由自己决定自己的尺寸,但是有个前提是它不能大于 父容器 本身给的建议值。
- // 并且需要设置它的测量模式为 AT_MOST
- resultSize = size;
- resultMode = MeasureSpec.AT_MOST;
- }
- break;
- // Parent has imposed a maximum size on us
- // 父容器是 AT_MOST 模式,对于 父容器 本身而言,它也有个最大的尺寸约束
- case MeasureSpec.AT_MOST:
- if (childDimension >= 0) {
- // Child wants a specific size... so be it
- // 如果 child 希望有自己的尺寸,那么允许它,并把它的测量模式设置为 EXACTLY
- resultSize = childDimension;
- resultMode = MeasureSpec.EXACTLY;
- } else if (childDimension == LayoutParams.MATCH_PARENT) {
- // Child wants to be our size, but our size is not fixed.
- // Constrain child to not be bigger than us.
- // child 希望尺寸和 父容器 一样大,那么允许它,但是 父容器 本身有限制,所以也
- // 需要给 child 加一个限制,这个限制就是将 child 模式设置为 AT_MOST
- resultSize = size;
- resultMode = MeasureSpec.AT_MOST;
- } else if (childDimension == LayoutParams.WRAP_CONTENT) {
- // Child wants to determine its own size. It can't be
- // bigger than us.
- // child 希望由自己决定自己的尺寸,但是有个前提是它不能大于 父容器 本身给的建议值。
- // 并且需要设置它的测量模式为 AT_MOST
- resultSize = size;
- resultMode = MeasureSpec.AT_MOST;
- }
- break;
- // Parent asked to see how big we want to be
- // 父容器是 UNSPECIFIED 模式,对于 父容器 本身而言,它的期望值是想要多大就多大,让 父容器 的 parent不要限制它。
- case MeasureSpec.UNSPECIFIED:
- if (childDimension >= 0) {
- // Child wants a specific size... let him have it
- //如果 child 希望有自己的尺寸,那么允许它,并把它的测量模式设置为 EXACTLY
- resultSize = childDimension;
- resultMode = MeasureSpec.EXACTLY;
- } else if (childDimension == LayoutParams.MATCH_PARENT) {
- // Child wants to be our size... find out how big it should
- // be
- // child 希望尺寸和 父容器 一样大,那么允许它,但是 父容器 本身也需要计算,所以只能设置为0,并且
- // 将child的测量模式设置为 UNSPECIFIED
- resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
- resultMode = MeasureSpec.UNSPECIFIED;
- } else if (childDimension == LayoutParams.WRAP_CONTENT) {
- // Child wants to determine its own size.... find out how
- // big it should be
- // child 希望尺寸由自己决定,一般这个时候,父容器 会给它一个 Size 作为最大值限制,
- // 但是 父容器 本身也需要计算,所以只能设置为0,并且没有给child最大值的设定
- // 将child的测量模式设置为 UNSPECIFIED
- resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
- resultMode = MeasureSpec.UNSPECIFIED;
- }
- break;
- }
- //noinspection ResourceType
- return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
- }
总结来说 mode 取值 受父容器与子控件共同决定
父容器是MeasureSpec.EXACTLY模式
子控件是具体值, mode = MeasureSpec.EXACTLY
子控件是MATCH_PARENT, mode = MeasureSpec.EXACTLY
子控件是WRAP_CONTENT, mode = MeasureSpec.AT_MOST
父容器是MeasureSpec.AT_MOST模式
子控件是具体值, mode = MeasureSpec.EXACTLY
子控件是MATCH_PARENT, mode = MeasureSpec.AT_MOST
子控件是WRAP_CONTENT, mode = MeasureSpec.AT_MOST
父容器是MeasureSpec.UNSPECIFIED模式
子控件是具体值, mode = MeasureSpec.EXACTLY
子控件是MATCH_PARENT, mode = MeasureSpec.UNSPECIFIED
子控件是WRAP_CONTENT, mode = MeasureSpec.UNSPECIFIED
就这样