Android-CustomView onMeasure
onMeasure
|
|
MeasureSpec
虽然表面上看起来他们是int类型的数字,其实他们是由mode+size两部分组成的。
widthMeasureSpec和heightMeasureSpec转化成二进制数字表示,他们都是32位的。前两位代表mode(测量模式),后面30位才是他们的实际数值(size)。
MeasureSpec的值由specSize和specMode共同组成的,其中specSize记录的是大小,specMode记录的是规格。
- 模式分类
它有三种模式:
- UNSPECIFIED(未指定),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;
表示父视图希望子视图的大小应该是由specSize的值来决定的,系统默认会按照这个规则来设置子视图的大小,开发人员当然也可以按照自己的意愿设置成任意的大小。 - EXACTLY(完全),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
- AT_MOST(至多),子元素至多达到指定大小的值。
他们对应的二进制值分别是:
UNSPECIFIED=00000000000000000000000000000000
EXACTLY = 01000000000000000000000000000000
AT_MOST =10000000000000000000000000000000
由于最前面两位代表模式,所以他们分别对应十进制的0,1,2;
- 模式提取
现在我们知道了widthMeasureSpec和heightMeasureSpec是由模式和数值组成的,而且二进制的前两位代表模式,后28位代表数字。
我们先想想,如果我们自己来提取widthMeasureSpec和heightMeasureSpec中的模式和数值是怎么提取呢?
首先想到的肯定是通过MASK和与运算去掉不需要的部分而得到对应的模式或数值。
说到这大家可能会迷茫,我们写段代码来提取模式部分吧:123456789//对应11000000000000000000000000000000;总共32位,前两位是1int MODE_MASK = 0xc0000000;//提取模式public static int getMode(int measureSpec) {return (measureSpec & MODE_MASK);}//提取数值public static int getSize(int measureSpec) {return (measureSpec & ~MODE_MASK);
概念
测量View大小使用的是onMeasure函数,我们可以从onMeasure的两个参数中取出宽高的相关数据:
测量模式
一共有三种, 被定义在 Android 中的 View 类的一个内部类View.MeasureSpec中:
模式 二进制数值 描述
UNSPECIFIED 00 默认值,父控件没有给子view任何限制,子View可以设置为任意大小。
EXACTLY 01 表示父控件已经确切的指定了子View的大小。
AT_MOST 10 表示子View具体大小没有尺寸限制,但是存在上限,上限一般为父View大小。
wrap_content-> MeasureSpec.AT_MOST
match_parent -> MeasureSpec.EXACTLY
具体值 -> MeasureSpec.EXACTLY
确定View的大小
确定View大小(onSizeChanged)
这个函数在视图大小发生改变时调用。
Q: 在测量完View并使用setMeasuredDimension函数之后View的大小基本上已经确定了,那么为什么还要再次确定View的大小呢?
A: 这是因为View的大小不仅由View本身控制,而且受父控件的影响,所以我们在确定View大小的时候最好使用系统提供的onSizeChanged回调函数。
onSizeChanged如下:
可以看出,它又四个参数,分别为 宽度,高度,上一次宽度,上一次高度。
这个函数比较简单,我们只需关注 宽度(w), 高度(h) 即可,这两个参数就是View最终的大小。
调用父类的onMeasure()
首先定义一个类继承View,重写onMeasure(),并调用父类onMeasure()方法。
重写onMeasure(),并调用父类onMeasure()时
- MeasureExampleView的layout_width以及layout_height属性值 match_parent 或者 wrap_content显示大小由其父容器控件决定。
- MeasureExampleView设置为固定的值,就显示该设定的值
怎么样重写onMesure
123456789101112//widthMeasureSpec 和 heightMeasureSpec的值 由父容器决定protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int width = measureDimension(DEFAULT_WIDTH, widthMeasureSpec);int height = measureDimension(DEFAULT_HEIGHT, heightMeasureSpec);setMeasuredDimension(width, height);}MeasureSpec.getSize()会解析MeasureSpec值得到父容器width或者height。MeasureSpec.getMode()会得到三个int类型的值分别为:MeasureSpec.EXACTLY MeasureSpec.AT_MOST,MeasureSpec.UNSPECIFIED。MeasureSpec.UNSPECIFIED 未指定,所以可以设置任意大小。
- MeasureSpec.getSize()会解析MeasureSpec值得到父容器width或者height。
- MeasureSpec.getMode()会得到三个int类型的值分别为:MeasureSpec.EXACTLY MeasureSpec.AT_MOST,MeasureSpec.UNSPECIFIED。
MeasureSpec.UNSPECIFIED 未指定,所以可以设置任意大小。
MeasureSpec.AT_MOST MeasureExampleView可以为任意大小,但是有一个上限。比如这种情况重写onMeasure()函数
我们前面讲过,onMeasure()的作用就是根据container内部的子控件计算自己的宽和高,最后通过setMeasuredDimension(int width,int height设置进去);