Android进阶知识(十二):View的工作原理之基本概念

Android进阶知识(十二):View的工作原理之基本概念

  从这一篇章开始,笔者将介绍关于View的工作原理与自定义View的相关内容。在介绍View的工作原理(三大流程)之前,读者有必要了解一些基本概念。

一、Activity的视图层结构

  在ActivityThread中,当Activity对象被创建完毕之后,会将DecorView添加到Window中。Window是一个抽象类,其唯一实现类为PhoneWindow,PhoneWindow将一个DecorView设置为整个应用窗口的*View
Android进阶知识(十二):View的工作原理之基本概念

  DecorView作为窗口界面的*View,封装了一些操作窗口的基本方法,其继承自FrameLayout(说明DecorView是一个ViewGroup),View层的事件都是先经过DecorView,然后才传递给View。值得一提的是,在Activity中通过setContentView设置的布局文件其实是被添加到id为content的内容栏中。
Android进阶知识(十二):View的工作原理之基本概念

二、ViewRoot

  ViewRoot对应于ViewRootImpl类,其为连接WindowManager和DecorView的纽带,View的三大流程均是ViewRoot来完成的。在ActivityThread中,当Activity对象被创建完毕之后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联(至于这些关联关系,笔者后续将单独做一篇介绍)。
  View的绘制流程是从ViewRoot的performTraversals方法开始的,其经过measure、layout和draw三个过程才能最终将一个view绘制出来。performTraversals的工作流程如下所示。
Android进阶知识(十二):View的工作原理之基本概念

  performTraversals会依次调用performMeasure、performLayout和performDraw三个方法,这三个方法分别完成*View的measure、layout和draw这三大流程。其中在performMeasure中会调用measure方法,在measure方法中又会调用onMeasure方法,在onMeasure方法中则会对所有子元素进行measure过程,这就把measure过程传递给子元素中。performLayout和performDraw的传递流程和performMeasure类似,唯一不同的是,performDraw的传递过程是在draw方法中通过dispatchDraw来实现的(具体的三大流程过程见下一篇笔记)。
Android进阶知识(十二):View的工作原理之基本概念

  Measure过程决定了View测量的宽/高,几乎所有情况下测量后的宽/高等同于View最终的宽高。Layout过程决定了View四个顶点的坐标和实际的View的宽/高,可通过getWith和getHeight获取最终宽/高。Draw过程决定了View的显示,只有draw方法完成以后View的内容才能呈现。

三、LayoutParams

  LayoutParams是布局参数类,其指定了视图View的高度和宽度等布局参数,可以通过以下参数指定。

参数 解释
具体值 dp/px
fill_parent 强制性使子视图的大小扩展至与父视图大小相等(不含padding)
match_parent 与fill_parent相同,用于Android 2.3之后版本
wrap_content 自适应大小,强制性地使视图扩展以便显示其全部内容(含padding)

四、理解MeasureSpec

  为了能够理解View的measure过程,对于参与View测量过程的MeasureSpec必须要有所了解,MeasureSpec在很大程度上决定了一个View的尺寸规格
  一个View的MeasureSpec一旦确定之后,onMeasure中就可以确定View的测量宽/高
Android进阶知识(十二):View的工作原理之基本概念

  1. MeasureSpec

  MeasureSpec代表一个32位int值,高2位代表SpecMode(测量模式),低30位代表SpecSize(某种测量模式下的规格大小)
Android进阶知识(十二):View的工作原理之基本概念

  MeasureSpec通过将SpecMode和SpecSize打包成一个int值来避免过多的对象内存分配。其中SpecMode 有三类,每一类都表示特殊的含义,如下所示。

模式 具体描述 应用场景
UNSPECIFIED 父容器不对View有任何限制(即View可取任意大小 系统内部,表示一种测量的状态
EXACTLY 父容器已经检测出View所需的精确大小,View的最终大小就是SpecSize所指定的值 对应于LayoutParams中的match_parent和具体数值这两种模式
AT_MOST 父容器指定一个可用大小即SpecSize,View的大小不能大于这个值 对应于LayoutParams中的wrap_content
  1. MeasureSpec和LayoutParams的对应关系

  上面提到了,MeasureSpec很大程度上决定了View的尺寸规格,这里之所以说很大程度,原因在于View的尺寸还和LayoutParams有关
  对于DecorView,其MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同决定
Android进阶知识(十二):View的工作原理之基本概念

  根据ViewRootImpl的源码,DecorView的MeasureSpec的产生过程比较明确,根据它的LayoutParams中的宽/高来划分如下表所示。

LayoutParams DecorView的MeasureSpec
MATCH_PARENT EXACTLY + 窗口大小
WRAP_CONTENT AT_MOST + 大小不定,但不能超过窗口大小
固定大小(比如100dp) EXACTLY + LayoutParams中指定大小

  而对于普通View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定
Android进阶知识(十二):View的工作原理之基本概念

  根据ViewGroup的相关源码,可以得到普通View的MeasureSpec的创建规则,如下表所示,其中表中的paresentSize是指父容器中目前可使用大小(除去padding和margin)。

parentSpecMode \ childLayoutParams EXACTLY AT_MOST UNSPECIFIED
dp/px EXACTLY + childSize EXACTLY + childSize EXACTLY + childSize
match_parent EXACTLY + parentSize AT_MOST + parentSize UNSPECIFIED + 0
wrap_content AT_MOST + parentSize AT_MOST + parentSize UNSPECIFIED + 0

参考资料:《Android开发艺术探索》
     自定义View Measure过程 - 最易懂的自定义View原理系列(2)