android在屏幕上绘制视图3步: measure测量、layout布局、draw绘制。
这里主要介绍第一步measure,measure是view大小计算的过程。先来一个自定义View的例子,演示如何覆写onMeasure方法
一、 覆写onMeasure的例子(自定义View)
例子下载地址 自定义view全屏显示蓝色方块
1. 覆写onMeasure
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- }
2. 分别计算出宽高,后面解释使用的方法的作用
- private int measureWidth(int pWidthMeasureSpec) {
- int result = 0;
- int widthMode = MeasureSpec.getMode(pWidthMeasureSpec);
- int widthSize = MeasureSpec.getSize(pWidthMeasureSpec);
- switch (widthMode) {
- case MeasureSpec.AT_MOST:
- case MeasureSpec.EXACTLY:
- result = widthSize;
- break;
- }
- return result;
- }
- private int measureHeight(int pHeightMeasureSpec) {
- int result = 0;
- int heightMode = MeasureSpec.getMode(pHeightMeasureSpec);
- int heightSize = MeasureSpec.getSize(pHeightMeasureSpec);
- switch (heightMode) {
- case MeasureSpec.AT_MOST:
- case MeasureSpec.EXACTLY:
- result = heightSize;
- break;
- }
- return result;
- }
3. 调用setMeasuredDimension,指定视图在屏幕上的大小、
- int measureWidth = measureWidth(widthMeasureSpec);
- int measureHeight = measureHeight(heightMeasureSpec);
- setMeasuredDimension(measureWidth, measureHeight);
二、 解释例子中使用的MeasureSpec
MeasureSpec是一个android.view.View的内部类。
MeasureSpec封装了从父类传送到子类的布局要求信息。每个MeasureSpec对象描述了空间的高度或宽度。 MeasureSpec由size和mode组成。
1. MeasureSpec的方法介绍:
类名.方法名 | 解释 |
MeasureSpec.getMode(int measureSpec) | 根据提供的测量值(格式)提取模式(上述三个模式之一) |
MeasureSpec.getSize(int measureSpec) | 根据提供的测量值(格式)提取大小值(这个大小也就是我们通常所说的大小) |
MeasureSpec.makeMeasureSpec(int size,int mode) | 根据提供的大小值和模式创建一个测量值(格式) |
2. MeasureSpec有三种mode,分别说明并描述模式与layout参数值的对应关系
模式 | 模式翻译 | 模式与Layout参数的对应关系 android:layout_width=“” android:layout_height=“” | 模式描述 |
UNSPECIFIED | 无限制 | parent view不约束child view的大小 | |
AT_MOST | 最多的 | wrap_content | parent view 为child view指定最大的值,child view可以在范围内设置 |
EXACTLY | 准确的 | fill_parent,精确值(例如50dip) | parent view为child view指定固定大小 |
三、 疑惑
例子总只写了一个view,在onMeasure中添加log获知此方法执行2次? 在ADT 20.3中同样的代码onMeasure执行4次?不是应该只执行一次吗?
我整明白了,当new ViewGroup()的时候,通过getWidth()和getHeight()(在构造方法里打log),得到的值首先是0,0,然后通过调用onMeasure()和onLayout()方法,会对这个view测量大小,这个时候view的宽高就发生了改变,这个时候又会重新调用一次onMeasure和onLayout方法(当view发生改变的时候,这两个方法会被调用),这时候你通过getWidth和getHeight方法就可以看到被测量之后的宽高了。这就是会调用两次的原因。
如果父视图的子视图的个数为0,就会执行一次。否则就会执行多次。因为开始时父试图中是没有子试图的。但是当你从xml文件中加载子试图或者在java代码中添加子试图时,父试图的状态会发生变化,这个变化会引起onlayout甚至是onmeasure。
四、 资料
官方资料:Custom Components
计算控件尺寸
MeasureSpec介绍 ( 如何计算size和mode )
重写onMeasure典型例子:
提高:
自定义view改成addview多个,每个layout参数不同
各种模式画图比较