先来看看MeasureSpec代码。
MeasureSpec
中UNSPECIFIED
,EXACTLY
和AT_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_MASK
为1100 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
类型的,并且只能存储mode
和size
这两种数据。
出处:(https://www.jianshu.com/p/a790982fd20e
从MeasureSpec类的定义我们知道,它封装了对子View的布局要求
mode代表的就是父容器对子View的布局要求。要求子View一个确定的值或给一个最大值,又或者不对其限制大小。
下面以一个例子说明MeasureSpec
的使用。
现有在一张20x20大小的纸,父容器大小为10x10。10x10的容器要求在其中画一个一个长度为10,宽度为5的长方形,由于已经给定了子View的大小,所以对应mode
为EXACTLY
,现需把这些信息存储到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
去存储mode
和size
这两个数据而已。
上面是View已经确定自身大小的示例,那么其他两种mode又是如何使用?
例如现有一20x20大小的纸,需在纸上画一片叶子,叶子大小未定,但是要求叶子不能超出纸张的大小,此时对应mode
为AT_MOST
。叶子最大可以定义为20x20,也可以是10x10。
再比如,一100x100大小的纸,选出20x20的区域,以20x20的区域为父容器画一片叶子,但是不限制叶子的大小,叶子大小可以为30x30,或10x10,此时对应mode
为UNSPECIFIED
。
参考链接:
Android View 全解析(二) -- OnMeasure
MeasureSpec 的分析
深入理解MeasureSpec
MeasureSpec之详细分析
Android自定义View基础:MeasureSpec类到底是什么?
MeasureSpec的理解和详尽源码分析
网友评论