您的位置:首页 > 移动开发 > Android开发

Android View(三)-MeasureSpec详解

2016-01-03 20:32 330 查看
    在接触View中,总是会或多或少的遇到MeasureSpec,那么今天我们就来看看MeasureSpec是什么。

一.MeasureSpec说明

1.sdk说明

A MeasureSpec encapsulates the layout requirements passed from parent to child.
Each MeasureSpec represents a requirement for either the width or the height.
A MeasureSpec is comprised of a size and a mode. There are three possible modes:
UNSPECIFIED
The parent has not imposed any constraint on the child. It can be whatever size it wants.
EXACTLY
The parent has determined an exact size for the child. The child is going to be given those bounds regardless of how big it wants to be.
AT_MOST
The child can be as large as it wants up to the specified size.


翻译:一个MeasureSpec封装传递由父控件传递到子控件的布局要求,每个MeasureSpec代表宽度或者高度的要求,一个MeasureSpec包含一个大小和模式,它有3种模式:

模式1,UNSPECIFIED,父控件不强制约束子控件,子控件可以任意大小;

模式2,EXACTLY,父控件决定子控件的大小,子控件被限制在一定的边界内,无论它有多大;

模式3,AT_MOST,子控件至多达到指定大小。

2.源码

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

/**
* Measure specification mode: The parent has not imposed any constraint
* on the child. It can be whatever size it wants.
*/
public static final int UNSPECIFIED = 0 << MODE_SHIFT;

/**
* Measure specification mode: The parent has determined an exact size
* for the child. The child is going to be given those bounds regardless
* of how big it wants to be.
*/
public static final int EXACTLY     = 1 << MODE_SHIFT;

/**
* Measure specification mode: The child can be as large as it wants up
* to the specified size.
*/
public static final int AT_MOST     = 2 << MODE_SHIFT;

/**
* Creates a measure specification based on the supplied size and mode.
*
* The mode must always be one of the following:
* <ul>
*  <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li>
*  <li>{@link android.view.View.MeasureSpec#EXACTLY}</li>
*  <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>
* </ul>
*
* <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's
* implementation was such that the order of arguments did not matter
* and overflow in either value could impact the resulting MeasureSpec.
* {@link android.widget.RelativeLayout} was affected by this bug.
* Apps targeting API levels greater than 17 will get the fixed, more strict
* behavior.</p>
*
* @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) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}

/**
* Extracts the mode from the supplied measure specification.
*
* @param measureSpec the measure specification to extract the mode from
* @return {@link android.view.View.MeasureSpec#UNSPECIFIED},
*         {@link android.view.View.MeasureSpec#AT_MOST} or
*         {@link android.view.View.MeasureSpec#EXACTLY}
*/
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}

/**
* Extracts the size from the supplied measure specification.
*
* @param measureSpec the measure specification to extract the size from
* @return the size in pixels defined in the supplied measure specification
*/
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
...
}


解释:
(1).“<< ” ,<< (左移运算符)—num << 1,相当于num乘以2(在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方);
(2)."&",& (按位与)—对两个整数的二进制形式逐位进行逻辑与 运算,原理:1|0=0,0|0=0,1&1=1;0&1=0等;
(3)."~",~(非)—0变1,1变0。
通过上面这几行代码,可以看出,MeasureSpec返回的是int类型的数值,该数值是32位,测试模式的位数为最高2位,低30位为实际测量值。
3.补充

    通过上面简单描述,我们大致了解了,MeasureSpec它有三种测量模式;

模式1,UNSPECIFIED,不指定View的大小,View想多大就多大,一般情况下,我们不会用到(在ListView、ScrollView等,需要滑动,内部子View的大小是不固定的,那么就需要用这个模式);

模式2,EXACTLY,View的大小是确定的,即我们在设置View的宽或高时,设置为“match_parent”或者具体的值(例如android:layout_width=“200dp”);

模式3,AT_MOST,该模式对应的是,我们在设置View的宽或高时,设置为“wrap_content”。

二.View的measure过程

View的measure过程是由measure()方法完成的,measure()方法是一个final类型的,意味着View子类不能重写该方法,measure()会去调用View的onMeasure(),完成测量。

    看看View的onMeasure()的源码,代码如下,

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
调用setMeasuredDimension()设置View的宽或高的测量值,还调用了getDefaultSize()设置默认值,

/**
* Utility to return a default size. Uses the supplied size if the
* MeasureSpec imposed no constraints. Will get larger if allowed
* by the MeasureSpec.
*
* @param size Default size for this view
* @param measureSpec Constraints imposed by the parent
* @return The size this view should be.
*/
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);

switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
代码比较简单,getDefaultSize()方法返回的是measureSpec中的specSize,specSize是测量大小,UNSPECIFIED模式返回的测量大小是传递进来的大小,而EXACTLY和AT_MOST这两种测量模式返回的值是一样的,都是测量的大小(View的最终的大小是Layout阶段确定的)。

    至此,MeasureSpec这个内部类,就讲解完了。相信大家现在对这个类已经有新的认识了!

PS:我们在自定义View的时候,要是不重写onMeasure()方法,当你设置View的宽或者高时,设置为“match_parent”或者“wrap_content”,会可能看到的效果其实都是一样的(大小都一样),这是因为在调用measure()方法,设置measureSpec时,View的宽或者高初始化的时候都为MATCH_PARENT。所以当我们在自定义View的时候,可能需要自己去实现以及计算三种测量模式下的测量大小。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: