关于android的MeasureSpec的解读「建议收藏」

关于android的MeasureSpec的解读「建议收藏」我们自定义控件的时候,除了需要复写onDraw()方法实现我们view的视图外,有时候还需要复写onMeasure()来进行对自定义控件的大小进行精确的计算,以便自定义控件能更好的显示其内容。onMeasure(intwidthMeasureSpec,intheightMeasureSpec)中的参数,是父控件传进来的对子控件大小进行说明和限制的信息,包含了size(大小)和mode(模

大家好,欢迎来到IT知识分享网。

我们自定义控件的时候,除了需要复写onDraw()方法实现我们view的视图外,有时候还需要复写onMeasure()来进行对自定义控件的大小进行精确的计算,以便自定义控件能更好的显示其内容。

onMeasure(int widthMeasureSpec, int heightMeasureSpec)中的参数,是父控件传进来的对子控件大小进行说明和限制的信息,包含了size(大小)和mode(模式)的两个信息。google工程师为了不另外开辟对象空间来存储这两个信息,于是android提供了MeasureSpec这个类来打包size(大小)和mode(模式)为int值,并提供了解包的方法供提取出size(大小)和mode(模式)这两个信息。

API17以及之前,这个类提供的打包方法非常简单,但是存在bug。之后的版本已经修复了这个问题。(看代码,你能发现这个bug么)

         / 
         * @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) {
            return size + mode;
        }

IT知识分享网

很容易看出这方法的参数 ,如果对换位置并不影响返回的结果。并且如果传入的其中一个值溢出,会导致测量结果的不准确。RelativeLayout就受到这个bug的影响。之后的版本,修复后的代码如下:

IT知识分享网public static int makeMeasureSpec(int size, int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }

方法是做了兼容,在API17版本之前还继续使用原来的实现方法。sUseBrokenMakeMeasureSpec代表当前API是否在17以及之前。 下面我们来看看完整的MeasureSpec类是如何进行打包和解包的。(这里只保留了关键代码,并做了注释)。

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

        /**
         * 该模式下,代表父控件并没有对子控件大小做任何限制,子控件可以想多大就多大。
         */
        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;

        //打包大小和模式,传入的mode必须是上面定义好的三种模式之一
        public static int makeMeasureSpec(int size, int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }

         //解包获取模式
        public static int getMode(int measureSpec) {
            return (measureSpec & MODE_MASK);
        }

        //解包获取大小
        public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }

    }

MODE_MASK:

11000000000000000000000000000000

UNSPECIFIED:

00000000000000000000000000000000

EXACTLY:

01000000000000000000000000000000

AT_MOST:

10000000000000000000000000000000

~MODE_MASK:

00111111111111111111111111111111

这里我们假设一下,当size等于50,mode为AT_MOST的时候,我们进行计算一下,首先是size部分:size&~MODE_MASK :

00000000000000000000000100010010
&
00111111111111111111111111111111
=
00000000000000000000000100010010

实际上没有变对吧。同样的,来看看mode部分:mode & MODE_MASK :

10000000000000000000000000000000
&
11000000000000000000000000000000
=
10000000000000000000000000000000

运算结果和模式原来的一样,然后整个计算就是很巧妙的,模式数据在前面,大小数据在后面,(size & ~MODE_MASK) | (mode & MODE_MASK)

00000000000000000000000100010010
|
10000000000000000000000000000000
=
10000000000000000000000100010010

上面的运算结果就包含了模式和大小的数据,模式数据存在头部,大小则存在尾部,取出的时候 ,我们看看如何。先看mode的取出方法: measureSpec & MODE_MASK:

10000000000000000000000100010010
&
11000000000000000000000000000000
=
10000000000000000000000000000000

然后取出size的运算:measureSpec & ~MODE_MASK

10000000000000000000000100010010
&
00111111111111111111111111111111
=
00000000000000000000000100010010

就这样,非常简单的使用逻辑运算进行打包和解包。现在回到打包的时候,打包的时候size部分的运算和mode部分的运算其实都没有变化(真的没有变化么),也就是说,makeMeasureSpec(int size, int mode)方法实际上可以简单点(少做点运算):

IT知识分享网public static int makeMeasureSpec(int size, int mode) {
			return size|mode;
//			return (size & ~MODE_MASK) | (mode & MODE_MASK);
		}

注释掉的一行是谷歌工程师写的,至于为么这么写,我想应该是为了方便看出和取出方法相对应吧,你觉得呢?哈哈,当然不是了,如果按照简单的做法和API17以及之前的做法是一样的。试想一下,假如传入的size是负值,最高位符号位为1,运算后会影响到之后的size和mode取值的正确性。大家可以自己运算一下。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/11681.html

(0)

相关推荐

发表回复

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

关注微信