MeasureSpec介绍

MeasureSpec作为View中的一个很关键的内部静态类,在View的绘制过程中起到了很重要的作用。MeasureSpec参与了View的measure过程,很大程度上决定了View的尺寸规格。
在测量过程中,Android系统会将View的布局参数(LayoutParams)根据父容器所施加的规则,转化为MeasureSpec。然后,再根据这个measureSpec来测量View的宽度/高度。
首先看一下MeasureSpec的具体源码。

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

        /** @hide */
        @IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
        @Retention(RetentionPolicy.SOURCE)
        public @interface MeasureSpecMode {}

        public static final int UNSPECIFIED = 0 << MODE_SHIFT;

        public static final int EXACTLY     = 1 << MODE_SHIFT;

        public static final int AT_MOST     = 2 << MODE_SHIFT;

        public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
                                          @MeasureSpecMode int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }

        public static int makeSafeMeasureSpec(int size, int mode) {
            if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
                return 0;
            }
            return makeMeasureSpec(size, mode);
        }

        @MeasureSpecMode
        public static int getMode(int measureSpec) {
            //noinspection ResourceType
            return (measureSpec & MODE_MASK);
        }

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

        static int adjust(int measureSpec, int delta) {
            final int mode = getMode(measureSpec);
            int size = getSize(measureSpec);
            if (mode == UNSPECIFIED) {
                // No need to adjust size for UNSPECIFIED mode.
                return makeMeasureSpec(size, UNSPECIFIED);
            }
            size += 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);
        }

        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();
        }
    }

SpecMode有三类:UNSPECIFIED, EXACTLY, AT_MOST
UNSPECIFIED:父容器不对View有任何限制,即要多大的空间就给多大空间。这种情况一般用于Android系统内部,表示一种测量的状态。
EXACTLY:父容器已经检测出View所需要的精确大小,这时View的最终大小就是SpecSize所指定的值。这种模式对应的就是LayoutParams中的match_parent和具体的数值这两种模式。
AT_MOST:对应于LayoutParams中的wrap_content,即父容器指定一个可用的SpecSize,View的大小不能大于这个值。

上文中已经说过,Android系统会将View的布局参数(LayoutParams)根据父容器所施加的规则,转化为MeasureSpec。然后,再根据这个measureSpec来测量View的宽度/高度。需要注意的是,MeasureSpec不是唯一由LayoutParams决定的,layoutParams需要和父容器一起决定View的MeasureSpec,从而进一步决定View的宽/高。需要特别说明的是,DecorView(顶级View)和普通View,转换过程略有不同。
DecorView:MeasureSpec由窗口的尺寸和其自身的LayoutParams共同决定;
普通View:MeasureSpec由父容器的MeasureSpec和自身的LayoutParams共同决定。
不管是哪种情况,一旦MeasureSpec确立之后,onMeasure方法就可以确定View的测量宽度和高度了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值