1、ViewRoot 和 DecorView
-
ViewRoot
对应于ViewRootImpl
类,是连接WindowManager
和DecorView
的纽带,View 的measure
,layout
,draw
都是通过 ViewRoot 来完成的。
View 的三大流程:measure 过程决定了 View 的宽/高,measure 完成后,可以通过
getMeasuredWidth()/getMeasuredHeight()
获取 View 测量后的宽/高,这个宽高几乎就是 View 最终的宽/高了,除了特殊情况之外;layout 过程决定了 View 的四个顶点的坐标和实际的宽/高,完成后可以通过getTop()
,getLeft()
,getRight()
,getBottom()
获取四个顶点的坐标,还可以通过getWidth()
和getHeight()
获取 View 的最终的宽/高;draw 过程就决定了 View 的显示,只有draw()
完成才能将 View 的内容呈现在屏幕上。DecorView 其实是一个 FrameLayout,里面包含一个 LinearLayout,这个 LinearLayout 包含两个子View,一个是 Titlebar,另一个是 id 为 android.R.id.content 的 FrameLayout,可以通过
ViewGroup content = (ViewGroup) findViewById(android.R.id.content);
来获取这个 contentView,然后通过content.getChild(0)
来获取我们设置进去的 View。
2、MeasureSpec
MeasureSpec 代表一个 32 位 int 值,高 2 位代表 SpecMode(测量模式),低 30 位代表 SpecSize(某种测量模式下的规格大小)。
MeasureSpec 有 3 类:
UNSPECIFIED:父容器不对 View 做任何限制,要多大给多大。这种情况一般用于系统内部,表示一种测量状态。
EXACTLY:父容器已经检测出 View 所需要的大小,View 的大小就是 SpecSize 指定的值。这种情况对应于 LayoutParams 的 match_parent 和具体数值两种情况。
AT_MOST:父容器指定了一个可用的大小 SpecSize,View 最大不能超过这个 SpecSize 的值。这种情况对应于 LayoutParams 的 wrap_content。
注意:MeasureSpec 不是由该 View 的 LayoutParams 唯一决定的,而是在父容器的约束下(父容器的 MeasureSpec),将该 View 的 LayoutParams 转换成对应的 MeasureSpec,从而决定该 View 的测量后(onMeasure())的宽/高。
普通 View 的 MeasureSpec 的确定方法:
当 View 的 LayoutParam 是固定的宽/高的模式时,不管父容器的 MeasureSpec 是什么,View 的 MeasureSpec 都是 EXACTLY(精确模式)并且其大小遵循 LayoutParams 的大小
当 View 的宽/高是
match_parent
时,如果父容器的 MeasureSpec 是精准模式,View 的 MeasureSpec 也是 EXACTLY(精确模式)并且其大小就是父容器的剩余空间;如果父容器是 AT_MOST(最大模式),那么View 的 MeasureSpec 也是 AT_MOST(最大模式)并且其大小不会超过父容器的剩余空间当 View 的宽/高是
wrap_content
时,不管父容器的 MeasureSpec 是精准模式还是最大模式,View 的 MeasureSpec 都是 AT_MOST(最大模式)并且其大小不会超过父容器的剩余空间
3、View 的工作原理
-
measure 过程
measure 过程分两种情况,第一种是只是一个原始的 View ,那么通过 measure 方法就完成了其测量过程;如果是一个 ViewGroup,除了完成自己的测量过程外,还要遍历去调用所有子元素的 measure 方法,各个子元素再递归执行这个流程。
获取 View 测量后的宽/高的方法:
1.Activity/View # onWindowFocusChanged
2.view.post(runnable)
3.ViewTreeObserver
4.view.measure(int widthMeasureSpec,int heightMeasureSpec)
-
layout 过程
layout 是 ViewGroup 用来确定子元素的位置的,当 ViewGroup 的位置被确定后,在 onLayout()中会遍历所有子元素并调用其 layout 方法,在 layout 方法中 onLayout() 被调用。
父容器确定了自己的位置,即四个顶点的坐标之后,确定子元素的位置。
-
draw 过程
1.绘制背景
background.draw(canvas);
2.绘制自己ondraw();
3.绘制 childrendispatchDraw();
4.绘制装饰onDrawScrollBars();