ViewRoot对应于ViewRootImpl 类,它是连接WIndowManager 和 DecorView的纽带,View的三大流程均是通过VIewRoot来完成的,在ActivityThread中,当Activity对象被创建后,会将DecorView 添加到Window中,同时会创建VIewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联
View 的绘制流程是从ViewRoot的 performTraversals 方法开始的,performTraversals 依次调用 performMeasure、performLayout 和 performDraw,这三个方法分别完成顶级View的measure、layout 和 draw
performMeasure -> measure -> onMeasure,对子元素进行measure(layout 和 draw类似)
measure过程决定了View的宽/高,getMeasuredwidth/getMeasuredHeight,除特殊情况外它都等于View的最终宽/高;Layout过程决定View的四个顶点坐标和实际宽/高,getTop/getBottom/getLeft/getRight/getWidth/getHeight;Draw决定View的显示
DecorView作为顶级View,包含上下两个部分,上面是标题栏,下面是内容栏,Activity中 setContentView 所设置的布局是被加到内容栏;View层的事件都先经过DecorView 然后传递给我们的View
MeasureSpec代表一个32位的int值,高 2 位代表SpecMode 测量模式,低30位代表 SpecSize 规格大小
UNSPECIFIED:父容器不对View有任何限制,要多大有多大,一般用于系统内部
EXACTLY:父容器已经检测说View所需要的的精确大小,View的最终大小就是SpecSize指定的值,对应于 match_parent 和 具体数值两种模式
AT_MOST:父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,对应于 wrap_content
对于顶级View(DecorView),其MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同确定;
对于普通View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同确定
View的measure过程由ViewGrou传递而来,ViewGroup 的 measureChildWithMargins
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
View 的measure过程由其measure方法来完成,measure方法是一个final类型的方法,子类不能重写,measure方法中会去调用 onMeasure->setMeasuredDimension方法会设置View 宽/高的测量值
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
ViewGroup 除了完成自己的measure,还会遍历去调用所有子元素的measure,各个子元素再递归去执行这个过程。ViewGroup 是一个抽象类,没有重写 onMeasure,其测量过程的onMeasure 方法需要各个子类去具体实现,但提供了 measureChildren方法,ViewGroup 在measure时,会对每一个子元素进行measure
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
final int size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; ++i) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
如果想在onCreate等方法里获取View的宽高,有4种方式:
onWindowFocusChanged 表示View已经初始化完毕,宽高已经准备好,在Activity的窗口得到和失去焦点时均会被调用
ViewGroup的位置被确定后,onLayout中会遍历所有的子元素并调用其 layout方法,在layout方法中 onLayout方法又会被调用;layout方法确定View 本身的位置,onLayout方法确定所有子元素的位置
View 和 ViewGroup 均没有真正实现 onLayout