每一个 View 的绘制过程都必须经历三个最主要的过程,也就是 measure、layout 和 draw。整个 View 树的绘图流程是在 ViewRootImpl 类的 performTraversals() 方法开始的。
measure
MeasureSpec(View 的内部类)测量规格为 int 型,值由高 2 位规格模式 specMode 和低 30 位具体尺寸 specSize 组成。其中 specMode 只有三种值:EXACTLY 父 View 希望子 View 的大小是确定的,由 specSize 决定,如 match_parent;AT_MOST 父 View 希望子 View 的大小最多是 specSize 指定的值,如 wrap_content;UNSPECIFIED 未指定模式,父 View 完全依据子 View 的设计值来决定。
View 的布局大小由父 View 和子 View 共同决定。
使用 View 的 getMeasuredWidth() 和 getMeasuredHeight() 方法来获取 View 测量的宽高,必须保证这两个方法在 onMeasure 流程之后被调用才能返回有效值。
layout
整个 layout 过程比较容易理解,layout 是从顶层父 View 向子 View 的递归调用 view.layout 方法的过程,即父 View 根据上一步 measure 子 View 所得到的布局大小和布局参数,将子 View 放在合适的位置上。
measure 操作完成后得到的是对每个 View 经测量过的 measuredWidth 和 measuredHeight,layout 操作完成之后得到的是对每个 View 进行位置分配后的 mLeft、mTop、mRight、mBottom,这些值都是相对于父 View 来说的。
使用 View 的 getWidth() 和 getHeight() 方法来获取 View 测量的宽高,必须保证这两个方法在 onLayout 流程之后被调用才能返回有效值。
draw
如果该 View 是一个 ViewGroup,则需要递归绘制其所包含的所有子 View。
View 默认不会绘制任何内容,真正的绘制都需要自己在子类中实现。
invalidate & requestLayout
invalidate 会重绘。
requestLayout 会 measure 和 layout。
merge、include、ViewStub
merge 标签在 UI 的结构优化中起着非常重要的作用,它可以删减多余的层级,优化 UI。merge 多用于替换当一个布局包含另一个布局时,merge 标签消除视图层次结构中多余的层级。
include 就是为了解决重复定义相同布局的问题。例如你有五个界面,这五个界面的顶部都有布局一模一样的一个返回按钮和一个文本控件,在不使用 include 的情况下你在每个界面都需要重新在 xml 里面写同样的返回按钮和文本控件的顶部栏,这样的重复工作会相当的恶心。使用 include 标签,我们只需要把这个会被多次使用的顶部栏独立成一个 xml 文件,然后在需要使用的地方通过 include 标签引入即可。
ViewStub 就是一个宽高都为 0 的一个 View,它默认是不可见的,只有通过调用 setVisibility 函数或者 Inflate 函数才会将其要装载的目标布局给加载出来,从而达到延迟加载的效果,这个要被加载的布局通过 android:layout 属性来设置。