View的绘制流程
-
View的三大流程是通过ViewRoot来完成的
-
当Activity对象被创建后,会将DecorView添加到Window中,然后会将ViewRootImpl和DecorView建立起关联。
-
View的绘制流程从ViewRoot的performTraversals开始,这个方法会依次调用performMeasure、performLayout、performDraw方法,这些方法分别调用DecorView的measure、layout、draw方法完成DecorView的绘制
-
measure方法:测量view的宽和高
-
layout方法:用来确定子View在父viewgroup中的位置
-
draw方法:负责将view绘制
-
-
其中Viewgroup(decorView)的measure方法中会调用到他的onMeasure方法,在这个方法中会调用所有子元素的measure方法来对子元素进行测量宽和高,这样就把测量流程传递到子元素中,子元素重复父容器的测量流程,从而把所有view都进行测量
-
layout和draw流程和measure类似
MeasureSpec
-
MeasureSpec是一个32位的整型
-
高2位表示MeasureMode
-
UNSPECIFIED:用于系统内部,意思是要多大有多大
-
EXACTLY:对应match_parent和具体值
-
AT_MOST:对应wrap_parrent,当前不确定大小,但是不能超过父容器大小
-
-
剩下的30位表示MeasureSize,这个size可能不是最终测量的结果,更多时候是用来做测量的参考的
-
-
通过MeasureSpec可以分别获得mode和size
-
子元素的MeasureSpec 通过LayoutParams和父容器的MeasureSpec共同决定
-
LayoutParams其实就是我们在布局中指定的宽高的值
-
子view的MeasureSpec 会根据LayoutParams中得到的宽和高和父容器的MeasureSpec来确定
-
如果LayoutParams参数是match 或者 wrap,则size就是父容器的measureSize,并且mode分别为exactly和at_most
-
否则说明LayoutParams参数是一个固定的值,则size就是这个固定值,并且mode是exactly
-
这里需要扣除已经被占用的大小
-
Measure流程
-
view:measure --> onMeasure-->setMeasureDimension:对成员变量宽和高赋值
-
viewGroup:由于ViewGroup继承View,因此在对ViewGroup进行测量时,实际上调用的measure方法是父类View 中的measure,而measure中会调用onMeasure方法,然而ViewGroup并没用实现onMeasure方法,ViewGroup是一个抽象类,他的onMeasure方法会在不同的实现类中具体实现,因此onMeasure实际上是在LinearLayout,RelativeLayout等ViewGroup中实现的,而View的measure方法调用的onMeasure方法也就是调用这些ViewGroup实现类的onMeasure方法,在这个onMeasure方法中除了实现类本身的实现逻辑外,还会调用已经在ViewGroup里面实现了的measureChildren方法来对容器的子view进行测量,这个测量过程最终会调用子view的measure方法,将测量流程传递到子view中
-
最终view的宽和高会在OnMeasure之后的方法调用中进行赋值,也就是说可以在onLayout中获得测量之后的值,这个值可能不是最终的测量值
-
测量流程和活动的生命周期不是同步的,因此在生命周期回调中获取的测量值并不准确
-
这里可以在onWindowFocusChanged方法中获取测量好的值
-
也可以通过view.post一个runnable来获取测量值
Layout流程
-
layout:测量自身的位置(上下左右)
-
通过setFrame设置view的四个顶点的位置
-
-
onLayout:测量子元素的位置,递归调用子元素的layout,view和viewgroup并没有实现这个方法,同样在具体的实现类中实现。
-
几乎所有View的测量宽和高和最终宽高是相等的
Draw流程
-
调用draw方法
-
绘制背景
-
绘制自己(onDraw)
-
绘制child(dispatchDraw)
-
绘制装饰(onDrawScrollBars)
-