• 【Android源码剖析】(API 19)[View----->MeasureSpec]


    MeasureSpec

    MeasureSpec的全称是Measure Specification,意为“测量规格”。一个MeasureSpec对象封装了父布局传递给子布局的布局要求,每个MeasureSpec对象代表了一组宽度和高度要求。一个MeasureSpec对象由size和mode组成,MeasureSpec类通过将其封装在一个int值中以减少对象的分配。MeasureSpec的模式有三种:

    UNSPECIFIED

    父布局不对子元素做任何的约束,子元素可以得到任何想要的大小。

    EXACTLY

    子元素会以一个确定的值作为大小。

    AT_MOST

    子元素最多能是指定的大小。

    Constantsprivate static final int MODE_SHIFT

    该值指定了MeasureSpec三种模式切换时的进位大小,其默认值为30,意思是进位的大小为2的30次方。那么为什么会是30呢?我们知道一个int的长度大小为32位,而进位30位意思就是使用int的最高位和倒数第二位也就是32和31位作为标识位。

    private static final int MODE_MASK

    该值指定了MeasureSpec在进行位运算时的遮罩常量,其默认值为0x3 << MODE_SHIFT,也就是十六进制下的0x3左移30(MODE_SHIFT)位。十六进制下的0x3换位二进制即为11,左移30位即为:1100 0000 0000 0000 0000 0000 0000 0000。那这个遮罩的作用是什么?为什么要叫遮罩呢?其实其作用非常简单,只是用1标识需要的值而用0标识不需要的值,因为1与任何数做与运算都会得到该数而0与任何数做与运算都会得到0,就这么简单,这也就是为什么称其为遮罩,因为其能通过运算将不需要的值位“隐藏”起来而将需要的值位“显示”出来。

    public static final int UNSPECIFIED

    该值的意义开头有讲,这里就不重复累赘了。其默认值为0 << MODE_SHIFT,也就是十进制下的0左移30(MODE_SHIFT)位。十进制下的0换位二进制依然为0,左移30位也就是31个0,这里我们为了规范,将在左端补0凑够32位:0000 0000 0000 0000 0000 0000 0000 0000。

    public static final int EXACTLY

    该默认值为1 << MODE_SHIFT,也就是十进制下的1左移30(MODE_SHIFT)位。十进制下的1换位二进制依然为1,左移30位也就是1后跟30个0,这里我们为了规范,将在左端补0凑够32位:0100 0000 0000 0000 0000 0000 0000 0000。

    public static final int AT_MOST

    该默认值为2 << MODE_SHIFT,也就是十进制下的2左移30(MODE_SHIFT)位。十进制下的2换位二进制为10,左移30位也就是10后跟30个0:1000 0000 0000 0000 0000 0000 0000 0000。

    MeasureSpec的三种模式就介绍到这里,大家有木有发现?这三种模式包括MODE_MASK的值在二进制下都只占两位,移位后刚好在最高位和倒数第二位也就是32和31位上!有木有感触?结合上面对MODE_MASK的介绍自己脑补下,下面我们来看看MeasureSpec类的方法

    Methods

    在介绍MeasureSpec类的方法之前我先要在这介绍一下View类的onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法,该方法有两个int型的参数值widthMeasureSpec和heightMeasureSpec,这两个值我们输出一下看看:


    可以看到我们输出了两次相同的结果,也就是说onMeasure方法重复输出了两次,事实上该方法有可能会执行多次来对View进行测量,这具体得看View类的实现,我在讲View类的时候再细说,这里我们获取到onMeasure方法的两个参数值:

    widthMeasureSpec = 1073742144(十进制) = 0100 0000 0000 0000 0000 0001 0100 0000(二进制)

    heightMeasureSpec= 1073742260(十进制) = 0100 0000 0000 0000 0000 0001 1011 0100(二进制)

    上面我们说到,MeasureSpec类用一个int值存储了size和mode两个属性,也就是说上面两个参数值均包含了size和mode两个属性在里面,那么系统又是如何提取和封装它们的呢?这里我先将其转换为二进制并补全其至32位便于我们计算,下面来看MeasureSpec的方法。

    public static int getSize(int measureSpec)顾名思义,该方法返回了MeasureSpec的size值,其具体实现如下:

    [java] view plaincopyprint?

    1. return (measureSpec & ~MODE_MASK);  
    只是一个简单的位运算,我们来看看其具体计算过程。这里,我将上面的widthMeasureSpec 值传入该方法获取其返回值:

    getSize的输出结果为320(十进制) = 0000 0000 0000 0000 0000 0001 0100 0000(二进制),那我们是如何得到这个值的呢?首先,取反MODE_MASK:

    ~MODE_MASK = ~1100 0000 0000 0000 0000 0000 0000 0000 = 0011 1111 1111 1111 1111 1111 1111 1111

    再者,按位与:

    0100 0000 0000 0000 0000 0001 0100 0000(widthMeasureSpec)

    &

    0011 1111 1111 1111 1111 1111 1111 1111(~MODE_MASK )

    ------------------------------------------------------------

    0000 0000 0000 0000 0000 0001 0100 0000

    转为十进制即为320,大家发现没有?Android巧妙地用Mask将size值从低位“提取”了出来!getSzie方法的原理就很清晰了:MODE_MASK取反后将32,31替换成0也就是去掉mode保留后30位的size。heightMeasureSpec的值也是一样的方法提取,下面我们再来看看与getSize类似的另一个方法

    public static int getMode(int measureSpec)该方法与getSize类似,是从MeasureSpec值中获取mode值,这里我在布局文件中设置该自定义控件的layout_width为match_parent:

    [html] view plaincopyprint?

    1. <com.aigestudio.test.MyView  
    2.     android:layout_width="match_parent"  
    3.     android:layout_height="match_parent" />  
    同样我们将widthMeasureSpec传入该方法输出:

    可以得到mode的值为1073741824(十进制) = 0100 0000 0000 0000 0000 0000 0000 0000(二进制),与上面的三类模式对比不难发现与EXACTLY的值一样,我们来看看该方法的具体实现:

    [java] view plaincopyprint?

    1. return (measureSpec & MODE_MASK)  
    这里我就直接写过程了:

    0100 0000 0000 0000 0000 0001 0100 0000(widthMeasureSpec)

    &

    1100 0000 0000 0000 0000 0000 0000 0000(MODE_MASK )

    ------------------------------------------------------------

    0100 0000 0000 0000 0000 0000 0000 0000

    看到木有!看到这里大家明白这个MODE_MASK的作用了么?将mode存储在32和31位中而将size存储在后30位中!这也是为什么MODE_SHIFT为30的原因!不难理解吧……

    举一反三,下面我们将MyView的layout_width改为wrap_content看看:

    [html] view plaincopyprint?

    1. <com.aigestudio.test.MyView  
    2.     android:layout_width="wrap_content"  
    3.     android:layout_height="wrap_content" />  
    输出:

    得到mode的值为-2147483648(十进制) = 1000 0000 0000 0000 0000 0000 0000 0000(二进制),与上面的三类模式对比不难发现与AT_MOST的值一样,具体计算过程我就不写了。getMode方法的原理与上面的getSzie方法相反:用MODE_MASK后30位的0替换掉measureSpec后30位中的1再保留32和31位的mode值

    既然可以从MeasureSpec值中“提取”size和mode那必然也有对应的方法将其“封装”至MeasureSpec值中,下面我们就来看看这个方法

    public static int makeMeasureSpec(int size, int mode)

    [java] view plaincopyprint?

    1. if (sUseBrokenMakeMeasureSpec) {  
    2.     return size + mode;  
    3. } else {  
    4.     return (size & ~MODE_MASK) | (mode & MODE_MASK);  
    5. }  
    该方法只有一个判断结构体,判断值sUseBrokenMakeMeasureSpec在View类中声明,追踪后发现其在View的构造函数View(Context context)中被赋值:

    [java] view plaincopyprint?

    1. public View(Context context) {  
    2.     //……此处省略无关代码  
    3.     if (!sCompatibilityDone && context != null) {  
    4.         final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;  
    5.         sUseBrokenMakeMeasureSpec = targetSdkVersion <= JELLY_BEAN_MR1;  
    6.         //……此处省略无关代码  
    7.     }  
    8.     //……此处省略无关代码  
    9. }  
    在构造函数中先做了一个判断,boolean类型的sCompatibilityDone在View类中声明并赋值为false,也就是说该判断结构体在View被实例化时总会被执行,在其中先获取了TargetSdkVersion也就是我们在AndroidManifest.xml中android:targetSdkVersion字段下设置的值,当应用的TargetSdkVersion低于JELLY_BEAN_MR1也就是低于4.2的时候makeMeasureSpec方法会直接返回size + mode的值否则将其做位运算后在返回。这里的设计原理就是getSize和getMode的逆运算: 将mode的值存进32位二进制数的32和31位;将size的值存进32位二进制数的后30位,这样就能在上面的方法中分别提取size和mode值。

    static int adjust(int measureSpec, int delta)该方法是一个默认修饰符方法,仅供android.view包调用,View中不会直接调用上面的makeMeasureSpec方法但是会通过该方法的封装来间接地在measure方法中调用makeMeasureSpec方法,该方法会对View的布局长宽作调整,具体我在将View类的measure方法再说,其实现如下,其实看看计算过程你也能略知一二:

    [java] view plaincopyprint?

    1. return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec));  
    public static String toString(int measureSpec)这个方法不陌生撒,很好理解:

    [java] view plaincopyprint?

    1. int mode = getMode(measureSpec);  
    2. int size = getSize(measureSpec);  
    3.   
    4. StringBuilder sb = new StringBuilder("MeasureSpec: ");  
    5.   
    6. if (mode == UNSPECIFIED)  
    7.     sb.append("UNSPECIFIED ");  
    8. else if (mode == EXACTLY)  
    9.     sb.append("EXACTLY ");  
    10. else if (mode == AT_MOST)  
    11.     sb.append("AT_MOST ");  
    12. else  
    13.     sb.append(mode).append(" ");  
    14.   
    15. sb.append(size);  

    Tag:

    Freenovo 发表于2014-08-24 21:07:00 | 编辑 | 分享 0

    引用地址:

  • 相关阅读:
    POJ1087 A Plug for UNIX 网络流
    HDU4405 Aeroplane chess 期望DP
    POJ1062 昂贵的聘礼 区间枚举
    HDU4403 A very hard Aoshu problem 枚举
    HDU4407 Sum 容斥定理
    POJ2750 Potted Flower 线段树+DP
    软件项目开发流程以及人员职责
    10款最流行的开源Web设计工具
    C# 把\\r\\n替换为\r\n
    jquery validation 用到的输入验证正则表达式
  • 原文地址:https://www.cnblogs.com/freenovo/p/4469774.html
Copyright © 2020-2023  润新知