关于View Measure 的 MeasureSpec的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

就这样


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值