在做项目的时候需要自定义View,当时对onMeasure()还不是很了解,现在了解了一些,希望能和大家分享。


一、自定义View
自定义View的绘制
1.测量——onMeasure():决定View的大小
2.布局——onLayout():决定View在ViewGroup中的位置
3.绘制——onDraw():如何绘制这个View。

下面简单了解一下自定义View的绘制过程:
第一步:
当activity启动的时候,触发初始化view过程的是由Window对象的DecorView调用View(具体怎样从xml中读取是用LayoutInflater.from(context).inflate)对象的 public final void measure(int widthMeasureSpec, int heightMeasureSpec)方法开始的,这个方法是final类型的,也就是所有的子类都不能继承该方法,保证android初始化view的原理不变。具体参数类值,后面会介绍。

第二步:
View的measure方法onMeasure(widthMeasureSpec,heightMeasureSpec),该方法进行实质的view大小计算。view的大小是由父view和自己的大小据定的。onMeasure的实质就是把Match_parent和wrap_content转换为实际大小 。

第三步:
当measure结束时,回到DecorView,计算大小计算好了,那么就开始布局了,开始调用view的 public final void layout(int l, int t, int r, int b),该还是也是final类型的,目的和measure方法一样。layout方法内部会调用onlayout(int l, int t, int r, int b )方法,二ViewGroup将此方法abstract的了,所以我们继承ViewGroup的时候,需要重新该方法。该方法的本质是通过measure计算好的大小,计算出view在屏幕上的坐标点

第四步:
measure过了,layout过了,那么就要开始绘制到屏幕上了,所以开始调用view的 public void draw(Canvas canvas)方法,此时方法不是final了,原因是程序员可以自己画,内部会调用ondraw,我们经常需要重写的方法。


二、onMeasure()

一段例子:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(0,widthMeasureSpec), getDefaultSize(0,heightMeasureSpec));
        int childWidthSize = getMeasuredWidth();
        int childHeightSize = getMeasuredHeight();
        //高度和宽度一样
        heightMeasureSpec = widthMeasureSpec = MeasureSpec.makeMeasureSpec(childWidthSize, MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec,heightMeasureSpec);
    }

1、onMeasure的参数来源和代表的意思
onMeasure(widthMeasureSpec,heightMeasureSpec)
两个参数是父控件传递过来的,代表父控件的规格。
widthMeasureSpec:宽 详细 测量值
heightMeasureSpec:高 详细 测量值
MeasureSpec参数的值为int型,分为高32位和低16为,高32位保存的是specMode,低16位表示specSize

2、setMeasuredDimension( )
setMeasuredDimension用来存储测量的宽高值。

3、getDefaultSize( )

/**
   * 作用是返回一个默认的值,如果MeasureSpec没有强制限制的话则使用提供的大小.否则在允许范围内可任意指定大小
   * 第一个参数size为提供的默认大小,第二个参数为测量的大小
   */
  public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
    // Mode = UNSPECIFIED,AT_MOST时使用提供的默认大小
    case MeasureSpec.UNSPECIFIED:
      result = size;
      break;
    case MeasureSpec.AT_MOST:
    // Mode = EXACTLY时使用测量的大小   
    case MeasureSpec.EXACTLY:
      result = specSize;
      break;
    }
    return result;
  }

4、getMeasuredWidth( )
getMeasuredWidth用来得到某个View想要在parent view里面占的大小

5、MeasureSpec类
MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求。一个MeasureSpec由大小和模式组成。

三种模式:
MeasureSpec.UNSPECIFIED:父视图不对子视图施加任何限制,子视图可以得到任意想要的大小;
MeasureSpec.EXACTLY:父视图希望子视图的大小是specSize中指定的大小;
MeasureSpec.AT_MOST:子视图的大小最多是specSize中的大小。

它常用的三个函数:
static int getMode(int measureSpec):根据提供的测量值(格式)提取模式(上述三个模式之一)
static int getSize(int measureSpec):根据提供的测量值(格式)提取大小值(这个大小也就是我们通常所说的大小)
static int makeMeasureSpec(int size,int mode):根据提供的大小值和模式创建一个测量值(格式)