Android开发者必须要了解的View测量过程(View的工作过程之Measure过程)

View的工作原理之测量过程


目录

  • 1. 详细测量过程
    • 1.1 测量过程到底要测量什么?
    • 1.2 详细过程说明
    • 1.3 测量概念说明
      • 1.3.1 测量的是ViewGroup的宽高,不是内容的宽高
      • 1.3.2 测量的是View的宽高,不是内容的宽高
  • 2. 一般情况下的测量过程中要做的事情(通常的操作)
    • 2.1 测量
      • 2.1.1 ViewViewGroup不同测量
      • 2.1.2 MeasureSpec
      • 2.1.3 自定义实现测量
    • 2.2 设置测量的值
      • 2.2.1 View的默认实现
      • 2.2.2 getDefaultSize的实现

1. 详细测量过程

Android开发者必须要了解的View测量过程(View的工作过程之Measure过程)_第1张图片

1.1 测量过程到底要测量什么?

  1. View的宽 对应的属性为: mMeasuredWidth
  2. View的高 对应的属性为: mMeasuredHeight

1.2 详细过程说明

  1. 追溯到最根部,测量是从一个ViewGroup开始
  2. 先是调用到measure方法,在measure方法中回调onMeasure
  3. 如果onMeasure中对子节点进行了测量,则测量子节点

1.3 测量概念说明

1.3.1 ViewGroup测量的是ViewGroup的宽高,不是内容的宽高

ViewGroup嵌套View的情况下,测量的是ViewGroup的宽高,不是ViewGroup中内容的宽高。

Android开发者必须要了解的View测量过程(View的工作过程之Measure过程)_第2张图片

由上图可知,测量的数据上实际上是:

  1. 宽度 : parentWidth
  2. 高度 : parentHeight

因为内部内容的高度,或者宽度是可以大于容器的宽高的,所以可以这样认为,只有当宽高设置为wrap_content时,ViewGroup的测量才会与内部的View有关

1.3.2 View测量的是View的宽高,不是内容的宽高

一般情况下,View是用来显示一些内容的,那么测量View时,测量的也是View的宽高,而不是内容的宽高

Android开发者必须要了解的View测量过程(View的工作过程之Measure过程)_第3张图片

由上图可知,测量的数据上实际上是:

  1. 宽度 : width
  2. 高度 : height

因为内部内容的高度,或者宽度是可以大于容器的宽高的,所以如果如果想根据内容变化,需要计算内容的宽度和高度。

2. 一般情况下的测量过程中要做的事情(通常的操作)

  1. onMeasure中,根据实际情况,实现测量操作
  2. 将测量出来的值,通过setMeasuredDimension设置到View的属性上

Android开发者必须要了解的View测量过程(View的工作过程之Measure过程)_第4张图片

2.1 测量

测量过程实际上是根距实际的需求,定义算法来算出宽高,也是测量过程中比较重要的一步。由于measure方法是不可重新写的,所以,测量过程一般都是通过重写onMeasure方法来定义。

2.1.1 ViewViewGroup不同测量

ViewViewGroup不同,因为View没有子节点,只需要测量即可。而不同的ViewGroup会可能有不同的测量规则,因为它可能根据子节点来变化,所以一般ViewGroup需要测量子节点,而且还需要根据子节点的宽高来计算自己的宽高。ViewGroup提供了测量子节点的方法。

一般情况下,ViewGroup需要自定义测量过程,并且需要测量子节点。

2.1.2 MeasureSpec

在重写onMeasure时发现,传递了两个int值,这两个值是:int widthMeasureSpec, int heightMeasureSpec,实际上这是一个32位int值,高两位代表SpecMode,低30位代表SpecSize

  1. SpecMode指的是测量模式
  2. SpecSize指的是在某种测量模式下的规格大小

SpecMode有三个值,定义在MeasureSpec类中

  1. UNSPECIFIED,父容器不对View有任何限制,用于系统内部
  2. EXACTLY,精确值,这个时候View的最终大小就是SpecSize所指定的值, 对应于LayoutParam中的match_parent或者直接指定具体值。
  3. AT_MOST,父容器制定了一个可用大小,即SpecSizeView的大小不能大于这个值,对应于LayoutParams中的wrap_content
如何获取到SpecModeSpecSize呢??
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // 获取SpecMode
    MeasureSpec.getMode(widthMeasureSpec);
    MeasureSpec.getMode(heightMeasureSpec);
    // 获取SpecSize
    MeasureSpec.getSize(widthMeasureSpec);
    MeasureSpec.getSize(heightMeasureSpec);
    ......
}

2.1.3 自定义实现测量

onMeasure时,ViewGroup一般都需要实现此方法,并且要测量子节点。而View一般直接实现指定测量出的宽高即可。

但是有时候业务需求,需要自定义测量宽高,这时可以根据不同的测量模式进行不同的实现:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int pWidth=0;
    int pHeight=0;

     // 获取测量模式
    int widthMeasureSpecMode = MeasureSpec.getMode(widthMeasureSpec);
    int heightMeasureSpecMode = MeasureSpec.getMode(heightMeasureSpec);

    switch (widthMeasureSpecMode) {
    case MeasureSpec.UNSPECIFIED:
        ........ // 自定义实现
        break;
    case MeasureSpec.AT_MOST:
         ........  // 自定义实现
        break;
    case MeasureSpec.EXACTLY:
        ........ // 自定义实现
        break;
    }

    switch (heightMeasureSpecMode) {
    case MeasureSpec.UNSPECIFIED:
        ........ // 自定义实现
        break;
    case MeasureSpec.AT_MOST:
        ........ // 自定义实现
        break;
    case MeasureSpec.EXACTLY:
        ........ // 自定义实现
        break;
    }

    setMeasuredDimension(pWidth, pHeight);
}

2.2 设置测量的值

当回调onMeasure时,如果想设置自定义实现的测量后的结果,可以通过protected final void setMeasuredDimension(int measuredWidth, int measuredHeight)来设置,这个方法必须onMeasure()时被调用!

2.2.1 View的默认实现

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

看到,在View的默认实现中调用了getDefaultSize(int size, int measureSpec)方法用来获取宽高,

2.2.2 getDefaultSize的实现

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;
}



我的个人网站

  • Android模块
  • 开发知识分享

推荐视频教程:

Android从整体到局部系列视频教程戳我

广告:

我使用的装备:程序员必备 | 不伤关节 | 手感好 | 静电容 | Plum键盘|Niz键盘 戳我



你可能感兴趣的:(Android开发系列,Android,Notes)