MeasureSpec介绍

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

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的测量宽度和高度了。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/36464.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注