View 的测量 MeasureSpec

本文深入解析了Android中View的MeasureSpec机制,包括MeasureSpec的三种模式(UNSPECIFIED、EXACTLY、AT_MOST)、MeasureSpec的组成及计算方式,以及父View如何通过MeasureSpec控制子View的大小。同时,详细介绍了MeasureSpec与子View的LayoutParams之间的对应关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

View 的测量 MeasureSpec

我们平常自定义都是重写 view 的 onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,而这个方法传入的宽和高都是一个32位 int 值。下面我们揭开这层神秘的面纱。

MeasureSpec 解析

MeasureSpec 是位于 View 类中的一个静态类,也就 100 行左右。我们先来看其中的变量。

   public static class MeasureSpec {
   
        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

        /**
         * 测量指定模式:父容器不对子 view 施加任何约束,它可以是它想要的任何尺寸。
         * 一般系统中使用
         * 二进制值为:00000000000000000000000000000000
         */
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;

        /**
         * 测量规范模式:父容器确定子 view 的大小
         * 相当于子 view 的match_parent
         * 二进制值为:01000000000000000000000000000000
         */
        public static final int EXACTLY     = 1 << MODE_SHIFT;

        /**
         * 测量规范模式:父容器指定一个可用大小,子 view 的大小不能超过这个值。
         * 相当于子 view 的 wrap_content
         * 二进制值为:10000000000000000000000000000000
         */
        public static final int AT_MOST     = 2 << MODE_SHIFT;
	}

这三种模式都是通过对 MODE_SHIFT 进行左移得到的值,其二进制相应的值已经在注释中说明。所以说

View 的 MeasureSpec = SpecMode 模式(前两位) + SpecSize 尺寸(后30位)

MeasureSpec 方法

MeasureSpec 对外提供了一些静态方法,进行大小的打包和获取。

   public static class MeasureSpec {
   
       /**
        * 将 size 大小和 mode 模式打包,生成一个 int 值
        */
        public static int makeMeasureSpec(int size,nt mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }

        /**
         * 获取 MeasureSpec 中的模式
         */
        @MeasureSpecMode
        public static int getMode(int measureSpec) {
            //noinspection ResourceType
            return (measureSpec & MODE_MASK);
        }

        /**
         * 获取 MeasureSpec 中的尺寸
         */
        public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }
   
   }

父 View如何通过 MeasureSpec 控制子 View 大小

在 ViewGroup 中有一个 measureChild方法。可以看出,子 view 的 MeasureSpec 是通过父容器的 MeasureSpec 和子 view 的 LayoutParams所决定。最终会调用子 view 的 onMeasure方法。

protected void measureChild(View child, int parentWidthMeasureSpec,
            int parentHeightMeasureSpec) {
        final LayoutParams lp = child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom, lp.height);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

MeasureSpec 和子 view 的 LayoutParams 对应关系如下图
在这里插入图片描述

子 View 的测量

至此,我们开始说的 onMeasure()方法的两个参数来源都已经清楚了,再来看 View 中的 onMeasure()执行了哪些操作。

	/**
 	* 测量子 view 大小
 	*/
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
	setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
	}

	/**
	* 返回一个建议值
	*/
	protected int getSuggestedMinimumWidth() {
		return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
	}
	/**
	* 根据建议大小和measureSpec返回默认大小
	*/
    public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);

        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值