一、 view树的绘制流程
当anctivity接收到用户的触摸焦点时,会被请求绘制布局,这个请求时由android的framWork层来处理的。处理方法是从根节点开始测量和绘制。整个绘制流程是通过measure、layout和draw三个方法来实现的。
measure -> layout -> draw
- measure: 是否重新计算视图大小
- layout: 是否需要重新安置视图的位置
- draw: 是否需要重绘
总结:view树的绘制流程就像是一个递归过程,在onMeasure方法里,它的view会对所有它的子元素进行测量,测量过程就从它的父的viewGroup传递到了它的子view里面,经过子元素的递归,测量好了所有子元素的长度,然后进行递归,反复之后,就完成了整个父元素viewGroup的测量。而Layout 也是类似,树的递归过程。
二、 measure
measure的测量是一个树的递归过程,从上到下有序的进行遍历,根据父容器对子容器的测量规格及参数获取到子容器的长宽高,然后把子容器的长宽高返还给父容器进行统一的测量。
2.1 measure-重要参数
- ViewGroup.LayoutParams: 用来制定视图宽度和高度的参数,可以设置三种值,具体的值/match_parent/wrap_content。
- MeasureSpec: 测量规格,32位的int值,最高的2位表示spec_mode,是模式占位符,后面的30位表示测量规格的大小(在这种测量模式下尺寸的大小),在view空间Measure的过程中,系统将该View的LayoutParams结合父容器生成一个MeasureSpec,该MeasureSpec测量规格会规定好怎样去测量该View控件的大小。不管是ViewGroup.LayoutParams还是其他的大小,最终都会包装成MeasureSpec测量规格,然后返还给父容器,告诉父容器如何测量该View的空间大小。
mode 主要分为3类,分别是
- EXACTLY:父容器已经测量出子View的大小。对应是 View 的LayoutParams的match_parent 或者精确数值。
- AT_MOST:父容器已经限制子view的大小,View 最终大小不可超过这个值。对应是 View 的LayoutParams的wrap_content
- UNSPECIFIED:父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态。(这种不怎么常用)
2.2 measure-重要方法
- measure
在measure 方法,核心就是调用onMeasure( ) 进行View的测量。在自定义时,只要复写onMeasure方法就可以了 - onMeasure
- 参数1: widthMeasureSpec 宽的测量规格
- 参数2: heightMeasureSpec 高的测量规格
- setMeasureDimension
调用setMeasureDimension方法,该方法是测量阶段的终极,也是实现onMeasure的方法。
三、 layout
和measure是一样的,通过对view树的自上而下进行遍历,根据测量得到的尺寸来摆放子视图的位置,需要明确的是,子视图的具体位置都是相对父视图而言的。所以说view的onLayout方法是一个空实现。如果自定义view需要继承viewGroup,则必须实现onLayout方法,然后重新摆放自己想要自定义view的位置,在layout方法中,最终调用onLayout方法,ViewGroup的onLayout是一个抽象的实现了, 所以子View必须实现onLayout方法。
四、 draw
两个容易混淆的方法
- invalidate()
调用时请求android系统,如果视图大小没有发生变化,则不会调用layout放置这个过程, - requestLayout()
当布局发生变化时,如方向或尺寸变化时,就会调用requestLayout方法,在自定义视图中,经常会调用该方法,需要重新测量尺寸时就会手动调用该方法,调用方法后,会触发measure和layout过程,但是不会调用draw方法。
参考资料
Android View的绘制流程