MeasureSpec简单分析

2020-07-07  本文已影响0人  梧叶已秋声

先来看看MeasureSpec代码。
MeasureSpecUNSPECIFIEDEXACTLYAT_MOST定义如下。

    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;

        public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MODE_SHIFT) - 1) int size,
                                       int mode) {
            return (size & ~MODE_MASK) | (mode & MODE_MASK);
        }

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

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

int的取值范围是-2的31次方到2的31次方-1(java中int占4个字节,一个字节8位,那么int就是占32位,去掉符号位,能表示数据的为31位,所以是2的31次方),即-2147483648到2147483647。
直接打印出来结果如下。

D/MainActivity: UNSPECIFIED = 0
D/MainActivity: EXACTLY = 1073741824
D/MainActivity: AT_MOST = -2147483648

其中2 <<30结果是2147483648,由于超出int范围所以结果为-2147483648。
通过程序员计算器计算0 <<30,1 <<30和2 <<30 得出实际对应的二进制数据如下:

0000 0000 0000 0000 0000 0000 0000 0000
0100 0000 0000 0000 0000 0000 0000 0000
1000 0000 0000 0000 0000 0000 0000 0000

MODE_MASK1100 0000 0000 0000 0000 0000 0000 0000

定义这个3个int类型的数据有什么意义呢?

出处:https://woaitqs.cc/2016/10/18/2016-10-18-android-view-theory-2/
这里是利用了位运算,将一个 int 类型包含了两种信息,分别是 size 和 mode。java 的 int 类型,可以表示 32 位数字,最高的两位数字用来表示 mode,其余的部分用来表示 size。MeasureSpec 分别有三种 mode,分别是 UNSPECIFIED, EXACTLY 和 AT_MOST.

出处https://blog.csdn.net/cyp331203/article/details/45027641
①UNSPECIFIED:表示默认值,父控件没有给子view任何限制。------二进制表示:00
②EXACTLY:表示父控件给子view一个具体的值,子view要设置成这些值的大小。------二进制表示:01
③AT_MOST:表示父控件个子view一个最大的特定值,而子view不能超过这个值的大小。------二进制表示:10

简单来说MeasureSpec 有点类似于JavaBean类,用于存储数据,只是是int类型的,并且只能存储modesize这两种数据。

出处:(https://www.jianshu.com/p/a790982fd20e
从MeasureSpec类的定义我们知道,它封装了对子View的布局要求

mode代表的就是父容器对子View的布局要求。要求子View一个确定的值或给一个最大值,又或者不对其限制大小。

下面以一个例子说明MeasureSpec的使用。
现有在一张20x20大小的纸,父容器大小为10x10。10x10的容器要求在其中画一个一个长度为10,宽度为5的长方形,由于已经给定了子View的大小,所以对应modeEXACTLY,现需把这些信息存储到MeasureSpec 中。

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    int mWidth = 10;
    int mHeight = 5;
    int mWidthMeasureSpec;
    int mHeightMeasureSpec;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mWidthMeasureSpec = MeasureSpec.makeMeasureSpec(mWidth,MeasureSpec.EXACTLY);
        Log.d(TAG, "getMode = " + MeasureSpec.getMode(mWidthMeasureSpec) + " ,getSize = " + MeasureSpec.getSize(mWidthMeasureSpec));
        mHeightMeasureSpec = MeasureSpec.makeMeasureSpec(mHeight,MeasureSpec.EXACTLY);
        Log.d(TAG, "getMode = " + MeasureSpec.getMode(mHeightMeasureSpec) + " ,getSize = " + MeasureSpec.getSize(mHeightMeasureSpec));
    }

    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;

        public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MODE_SHIFT) - 1) int size,
                                          int mode) {
            return (size & ~MODE_MASK) | (mode & MODE_MASK);
        }


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

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

}

log如下所示。

D/MainActivity: getMode = 1073741824 ,getSize = 10
D/MainActivity: getMode = 1073741824 ,getSize = 5

MeasureSpec这个类简单来说就是通过makeMeasureSpec去存储modesize这两个数据而已。
上面是View已经确定自身大小的示例,那么其他两种mode又是如何使用?
例如现有一20x20大小的纸,需在纸上画一片叶子,叶子大小未定,但是要求叶子不能超出纸张的大小,此时对应modeAT_MOST。叶子最大可以定义为20x20,也可以是10x10。
再比如,一100x100大小的纸,选出20x20的区域,以20x20的区域为父容器画一片叶子,但是不限制叶子的大小,叶子大小可以为30x30,或10x10,此时对应modeUNSPECIFIED

参考链接:
Android View 全解析(二) -- OnMeasure
MeasureSpec 的分析
深入理解MeasureSpec
MeasureSpec之详细分析
Android自定义View基础:MeasureSpec类到底是什么?
MeasureSpec的理解和详尽源码分析

上一篇 下一篇

猜你喜欢

热点阅读