MeasureSpec到底是个什么东西

1,490 阅读3分钟

MeasureSpec在初学自定义View的时候有了解过,大致清楚作为onMeasure方法的两个参数,里面包含了父View分配的测量模式和可用size,但是具体的细节不太清楚。现在趁着看measure相关的源码的机会干脆理一理MeasureSpec

MeasureSpec是定义在View这个类中的内部静态类,方法不多,主要提供了测量相关参数解析和构建的方法。

MeasureSpec中的变量

  • MODE_SHIFT:值30,用于位移运算
  • MODE_MASK:值是3向左位移MODE_SHIFT位,用于计算测量模式的
  • UNSPECIFIED:值是0向左位移MODE_SHIFT位
  • EXACTLY:值是1向左位移MODE_SHIFT位
  • AT_MOST:值是2向左位移MODE_SHIFT位

从这些变量可以看出一个int值存储测量的相关信息,这个值的高两位用来存储测量模式,剩下的30位用于存储size。

MeasureSpec中的方法

makeMeasureSpec(int,int)

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

它有两个参数,一个size一个测量模式。可以看到这个方法通过变量sUseBrokenMakeMeasureSpec选择两种实现方式。而sUseBrokenMakeMeasureSpec由Android api等级决定。

// Older apps may need this compatibility hack for measurement.
sUseBrokenMakeMeasureSpec = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
  • size + mode

    在高两位和后面的尺寸互不干扰的情况下能够得出正确的MeasureSpec,这说明还有异常情况,从方法注释中可以了解到在Android api 17以前就是只使用了这种实现方式会产生bug。这就产生了第二种计算方式。

    //Android api 17的源码,可以看到没有做异常值的处理
    public static int makeMeasureSpec(int size, int mode) {
        return size + mode;
    }
    
  • (size & ~MODE_MASK) | (mode & MODE_MASK)

    (size & ~MODE_MASK)保留了size后30位的数据,抹掉了高两位的数据。(mode & MODE_MASK)以同样的方式保留了高两位的测量模式。最后通过|运算将两个数据合并(个人认为这里用+运算问题也不大,因为溢出的数据已经通过&运算处理了)。这里个人其实是有个疑问,既然这个方法的参数上都添加注解来限制输入了,为什么还要做这些处理,仔细看了方法的注释文档后看到这句makeMeasureSpec's implementation was such that the order of arguments did not matter意识到可能最开始并没有注解。

    //这是api 18的源码,可见并没注解,直到api 24才在参数的位置加上注解。
    public static int makeMeasureSpec ( int size, int mode){
        if (sUseBrokenMakeMeasureSpec) {
            return size + mode;
        } else {
            return (size & ~MODE_MASK) | (mode & MODE_MASK);
        }
    }
    

makeSafeMeasureSpec(int,int)

仅供内部使用,以与系统小部件和较旧的应用程序兼容。

getSize(int)

(measureSpec & ~MODE_MASK),位运算直接取出size

getMode(int)

(measureSpec & MODE_MASK),位运算取出高两位的测量模式

adjust(int, int)

这个方法只在View的measure方法中,测量光学边界的时候用到。具体逻辑是当测量模式不是UNSPECIFIED的时候讲size扩充delta的大小。但是目前还不清楚测量光学边界的作用是什么,个人感觉并不是特别重要的内容,所以安排以后再去了解。