Android小总结:MeasureSpec

本文详细解析了MeasureSpec的概念及其在自定义View中的应用。MeasureSpec是父View传递给子View的布局要求,包含size和mode两种属性,具体分为UNSPECIFIED、AT_MOST和EXACTLY三种模式。

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


在自定义一个View的时候,常常会使用MeasureSpec,MeasureSpec是什么呢,可以翻译为测量规范,测量标准,测量说明。

MeasureSpec是一个测量说明,表示父View传递给子View的布局要求和说明,封装了size和mode,而且包含了高度和宽度的size和mode。

MeasureSpec的mode有三种:

    /**
     * A MeasureSpec encapsulates the layout requirements passed from parent to child.
     * Each MeasureSpec represents a requirement for either the width or the height.
     * A MeasureSpec is comprised of a size and a mode. There are three possible
     * modes:
     *
     * 一个MeasureSpec类封装了从父View传递给子View的布局要求
     * 每个MeasureSpec类代表了一个宽度和高度的要求
     * 一个MeasureSpec类由size和mode组成,有三种可能的模式
     *
     * <dl>
     * <dt>UNSPECIFIED</dt>
     * <dd>
     * The parent has not imposed any constraint on the child. It can be whatever size
     * it wants.
     * </dd>
     *
     * <dt>EXACTLY</dt>
     * <dd>
     * The parent has determined an exact size for the child. The child is going to be
     * given those bounds regardless of how big it wants to be.
     * </dd>
     *
     * <dt>AT_MOST</dt>
     * <dd>
     * The child can be as large as it wants up to the specified size.
     * </dd>
     * </dl>
     * <pre>
     *
     * UNSPECIFIED(未指定):
     *                   父View没有对子View施加任何的约束,子View可以是任意大小
     * EXACTLY(精确):
     *                   父View已经为子View确定了具体的大小,
     *                   子View已经被给予了大小,忽略子View想要多大的尺寸
     * AT_MOST(最大):
     *                   子View可以和指定的大小一样大
     *  <pre/>
     * MeasureSpecs are implemented as ints to reduce object allocation. This class
     * is provided to pack and unpack the <size, mode> tuple into the int.
     *
     * 为减少对象分配,MeasureSpecs被实现为二进制整数
     * 此类提供了压缩和解压缩<size,mode>组到整数里
     * (也就是说可以把<size,mode>封装到MeasureSpecs中,也可以从MeasureSpecs中获取<size,mode>)
     */
    public static class MeasureSpec {
        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

        /**
         * Measure specification mode: The parent has not imposed any constraint
         * on the child. It can be whatever size it wants.
         *
         * 测量规范模式:父View没有给子View强加任何约束,子View可以是任何想要的尺寸。
         */
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;

        /**
         * Measure specification mode: The parent has determined an exact size
         * for the child. The child is going to be given those bounds regardless
         * of how big it wants to be.
         * 测量规范模式:父View已经给子View确定了具体的大小。 子View已经被给予了大小,忽略子View想要多大的尺寸
         */
        public static final int EXACTLY     = 1 << MODE_SHIFT;

        /**
         * Measure specification mode: The child can be as large as it wants up
         * to the specified size.
         *
         * 测量规范模式: 子View可以和指定的大小一样大
         */
        public static final int AT_MOST     = 2 << MODE_SHIFT;

        /**
         * Creates a measure specification based on the supplied size and mode.
         * 基于提供的size和mode创建一个测量规范
         *
         * The mode must always be one of the following:
         * mode必须为以下三者之一
         * <ul>
         *  <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li>
         *  <li>{@link android.view.View.MeasureSpec#EXACTLY}</li>
         *  <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>
         * </ul>
         *
         * <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's
         * implementation was such that the order of arguments did not matter
         * and overflow in either value could impact the resulting MeasureSpec.
         * {@link android.widget.RelativeLayout} was affected by this bug.
         * Apps targeting API levels greater than 17 will get the fixed, more strict
         * behavior.</p>
         * 笔记:在API 17和以下,makeMeasureSpec的实现,参数顺序没有问题,溢出值可能会影响MeasureSpec结果。
         *
         * @param size the size of the measure specification
         * @param mode the mode of the measure specification
         * @return the measure specification based on size and mode
         */
        public static int makeMeasureSpec(int size, int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }

        /**
         * Extracts the mode from the supplied measure specification.
         * 从提供的测量规范提取mode
         * @param measureSpec the measure specification to extract the mode from
         *                    从测量规范提取mode
         *
         * @return {@link android.view.View.MeasureSpec#UNSPECIFIED},
         *         {@link android.view.View.MeasureSpec#AT_MOST} or
         *         {@link android.view.View.MeasureSpec#EXACTLY}
         */
        public static int getMode(int measureSpec) {
            return (measureSpec & MODE_MASK);
        }

        /**
         * Extracts the size from the supplied measure specification.
         * 从提供的测量规范提取size
         * @param measureSpec the measure specification to extract the size from
         *                    从测量规范提取size
         * @return the size in pixels defined in the supplied measure specification
         *         单位:px,返回在提供的测量规范中的size
         */
        public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }

        static int adjust(int measureSpec, int delta) {
            final int mode = getMode(measureSpec);
            if (mode == UNSPECIFIED) {
                // No need to adjust size for UNSPECIFIED mode.
                return makeMeasureSpec(0, UNSPECIFIED);
            }
            int size = getSize(measureSpec) + delta;
            if (size < 0) {
                Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +
                        ") spec: " + toString(measureSpec) + " delta: " + delta);
                size = 0;
            }
            return makeMeasureSpec(size, mode);
        }

        /**
         * Returns a String representation of the specified measure
         * specification.
         *
         * 返回一个指定的测量规范的字符串表示。
         * @param measureSpec the measure specification to convert to a String
         *                    转换成String的测量规范
         * @return a String with the following format: "MeasureSpec: MODE SIZE"
         *                    返回一个String,带有如下格式:"MeasureSpec: MODE SIZE"
         */
        public static String toString(int measureSpec) {
            int mode = getMode(measureSpec);
            int size = getSize(measureSpec);

            StringBuilder sb = new StringBuilder("MeasureSpec: ");

            if (mode == UNSPECIFIED)
                sb.append("UNSPECIFIED ");
            else if (mode == EXACTLY)
                sb.append("EXACTLY ");
            else if (mode == AT_MOST)
                sb.append("AT_MOST ");
            else
                sb.append(mode).append(" ");

            sb.append(size);
            return sb.toString();
        }
    }

  1、 MeasureSpec.UNSPECIFIED,表示父View对子View大小没有指定,子view可以是任意的大小

                         当子View的宽高设置为0或者没有设置时,mode为未指定。

  2、 MeasureSpec.AT_MOST,表示子View的尺寸最大能达到多少。

                        当控件的layout_width或layout_height指定为WRAP_CONTENT时,子view大小一般随着他的子view或内容进行变化,此时控件尺寸只要不超过父view允许的最大尺寸即可。

  3、 MeasureSpec.EXACTLY,表示父控件给定了精确的尺寸。

                         当控件的layout_width或layout_height指定为具体数值或者为FILL_PARENT时,都表示控件大小是精确尺寸。

size和mode都是用二进制整数表示:

模式进位:

 private static final int MODE_SHIFT = 30;

在源码中,MODE_SHIFT表示mode进位转换,int型有32位,进位30位,表示用int型的31位和32位作为标识位。

31位二进制数为:

 /**
     |--------|--------|--------|--------|
      01000000 00000000 00000000 00000000
     |--------|--------|--------|--------|
     */

32位二进制数为:

/**
     |--------|--------|--------|--------|
      10000000 00000000 00000000 00000000
     |--------|--------|--------|--------|
     */

MODE_MASK:mode遮罩,就是说把其他不要的盖住,其实是二进制的与运算。0x3为十六进制,十进制为3

private static final int MODE_MASK  = 0x3 << MODE_SHIFT; MODE_SHIFT 进位30位表示二进制数就是:

    /**
     |--------|--------|--------|--------|
      11000000 00000000 00000000 00000000
     |--------|--------|--------|--------|
     */

public static final int UNSPECIFIED = 0 << MODE_SHIFT; 0左移30位表示二进制数为:

    /**
     |--------|--------|--------|--------|
      00000000 00000000 00000000 00000000
     |--------|--------|--------|--------|
     */
 public static final int EXACTLY     = 1 << MODE_SHIFT;1左移30位表示二进制数为:
    /**
     |--------|--------|--------|--------|
      01000000 00000000 00000000 00000000
     |--------|--------|--------|--------|
     */
public static final int AT_MOST     = 2 << MODE_SHIFT;2左移30位表示二进制数为:

    /**
     |--------|--------|--------|--------|
      10000000 00000000 00000000 00000000
     |--------|--------|--------|--------|
     */
生成测量说明:测量说明是尺寸和模式的二进制数相加得到的
 public static int makeMeasureSpec(int size, int mode) {
        if (sUseBrokenMakeMeasureSpec) {
            return size + mode;
        } else {
            return (size & ~MODE_MASK) | (mode & MODE_MASK);
        }
    }

获取模式,返回值为测量说明和MODE_MASK相与得到的

   public static int getMode(int measureSpec) {
        return (measureSpec & MODE_MASK);
    }
获取尺寸,返回值为测量说明和MODE_MASK取反值再相与得到的

   public static int getSize(int measureSpec) {
        return (measureSpec & ~MODE_MASK);
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值