1,ViewRoot和DecorView:
ViewRoot对应于ViewRootImpl类,是连接WindowManager和DceorView的纽带,View的三大流程(measure,layout,draw)都通过ViewRoot来完成.ActivityThread中,当Activity对象创建完毕后,会将DecorView添加到Window中,同时创建ViewRootImpl对象,并将ViewRootImpl和DecorView建立联系.View的绘制流程是从ViewRoot的performTraversals方法开始,经过measure(测量View的宽高),layout(确定View在父控件中的位置),draw(将View绘制在屏幕上)三个过程最终将一个View绘制出来.performTraversals依次调用performMeasure,performLayout,performDraw三个方法.
Measure决定View的宽高,之后就可以通过调用getMeasureWidth和getMeasureHight方法获取到测量后的宽高.
Layout过程决定了View的四个顶点的坐标和实际的View宽高,完成后可通过getTop,getBottom,getLeft,getRight获取到View的四个顶点位置,通过getWidth和getHight获取到View的最终宽高.
Draw决定了View的显示,draw完成后View的内容才能呈现在屏幕上.
DecorView作为顶级View,一般情况下会包含一个竖直方向上的LinerLayout,这个LinerLayout包含上下两个部分(具体情况与Android版本及主题有关),上面标题栏,下面内容栏.在Activity中通过setContentView所设置的布局文件内容被添加到内容栏,内容栏的id是content,因此可以理解Activity指定布局的方法不叫setview而叫setContentView.可以通过ViewGroup content = (ViewGroup)findViewById(android.R.id.content)得到content.可以通过content.getChildAt(0)得到我们设置的View.DecorView是一个FrameLayout,Veiw层的事件都先经过DecorView然后再传递给我们的View.
2,MeasureSpec
MeasureSpec解包可以得到SpecMode,SpecSize,同时这两个值也可打包成MeasureSpec(其所代表的int值,而非MeasureSpec本身)
SpecMode有三类:
UNSPECIFIED:父容器不对View有任何限制,要多大给多大,一般用于系统内部,表示测量状态
EXACTLY:父容器已经检测出View所需的精确大小,这个时候View的大小是SpecSize所指定的值,它对应于LayoutParams中的match_parent和具体数值的两种状态
AT_MOST:父容器指定了一个可用大小及SpecSize,View大小不能大于这个值,具体值要看View的具体实现,相当于LayoutParams中的wrap_content.
MeasureSpec和LayoutParams对应关系:
系统内部是通过MeasureSpec进行测量View,但正常情况下我们用View指定MeasureSpec,尽管如此我们可以给View设置LayoutParams.在View测量的时候,系统会将LayoutParams在父容器的约束下转换成对应的MeasureSpec,然后再根据这个MeasureSpec来确定View测量后的宽高,需要注意MeasureSpec是不唯一由LayoutParams决定的,LayoutParams需要和父容器一起才能决定View的MeasureSpec,从而进一步决定View的宽高.对于DecorView,其MeasureSpec由窗口的尺寸和自身的LayoutParams共同决定;对于普通的View其MeasureSpec由父容器和自身的LayoutParams共同决定,MeasureSpec一旦确定后,onMeasure中就可以确定View的测量宽高.DecorView的MeasureSpec产生根据LayoutParams中的宽高划分
LayoutParams.MATCH_PARENT:精确模式,大小就是窗口的大小
LayoutParams.WRAP_CONTENT:最大模式,大小不定,但是不能超过窗口的大小.
固定大小(如100dp):精确模式,大小为LayoutParams中指定的大小
普通View会先通过调用其ViewGroup的measureChildWithMargins获取其MeasureSpec,其与父容器的MeasureSpec和其自身的LayoutParams有关,同时还同View的margin和padding有关.
当View采用固定宽高的时候,不管父容器的MeasureSpec是什么,View的MeasureSpec都是精确模式并且大小遵循LayoutParams中的大小.当View的宽高是match_parent时,如果父容器的模式是精准模式,那么View也是精确模式并且大小是父容器的剩余空间;如果父容器是最大模式,那么View也是最大模式,并且大小不会超过父容器的剩余空间.当View的宽高是wrap_content时,不管父容器的模式是精准还是最大,View的模式都是最大化并且不能超过父容器的剩余空间.
因此只要提供了父容器的MeasureSpace和View的LayoutParams就可以很快的确定出子元素的MeasureSpec,有了MeasureSpec就可以进一步确定出子元素的大小.