Activity要作的事情比较多,为了解耦以及复用,将外观和行为的管理放到window
中,让window
负责view
的建立管理以及与viewRootImpl
的交互。java
PhoneWindow
:window
的惟一实现。DecorView
:window
的顶层视图,包含窗口装饰。继承自FrameLayout
,是一个视图真实的根。和window互相持有对方。ContentParent
:DecorView
的子view,放置窗口内容视图。便可以认为是DecorView
自己,也能够认为它是DecorView
的用来存放内容视图的子级。当在Activity
中经过setContentView()
设置视图的时候,会层层传递到PhoneWindow.setContentView()
中,经过addView()
或者inflate()
添加到ContentParent
中。canvas
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (...) {
...
} else {
mContentParent.addView(view, params);
}
...
}
复制代码
DecorView
是最顶层的View
,是视图树的的根,整个视图树的绘制从这里开始。安全
DecorView
的绘制由ViewRootImpl
控制,ViewRootImpl.performTraversals()
前后调用了performMeasure()
、peformLayout()
、performDraw()
,在其中,分别对应调用了DecorView
的measure()
、layout()
、draw()
方法,进行对应的绘制流程。markdown
private void performTraversals() {
...
performMeasure();
...
performLayout();
...
performDraw();
}
private void performMeasure() {
...
mView.measure();
}
private void performLayout() {
...
mView.layout();
}
private void performDraw() {
...
mView.draw();
}
复制代码
ViewRootImpl
工做的起点是在ViewRootImpl.setView()
被调用的时候。ViewRootImpl.setView()
主要的工做是:布局
将DecorView
存入ViewRootImpl
。post
调用requestLayout()
启动绘制。this
将ViewRootImpl
做为parent
存入DecorView
,完成相互的绑定。spa
void setView(View view,...) {
if (mView == null) {
mView = view;
...
requestLayout();
...
view.assignParent(this);
}
}
复制代码
ViewRootImpl.addView()
调用链:线程
ActivityThread.handleResumeActivity()
WindowManagerImpl.addView()
WindowManagerGlobal.addView()
,在其中建立ViewRootImpl
并调用ViewRootImpl.setView()
根据这个调用链能够知道,resume
以后才开始进行绘制流程,因此以前view
的宽高并无计算出来,读取不到view
的宽高。另外,在resume
以前的操做并不必定真正的绘制出来。code
ViewRootImpl.requestLayout()
调用的时候,不是当即开始绘制,而是将回调绑定到下一帧,在指定的时刻开始回调到performTraversals()
,开始绘制。另外能够看到ViewRootImpl.requestLayout()
中才进行了线程检查,这就解释了在ViewRootImpl
被初始化以前,不会进行线程安全的检查,即在初始化以前在其余线程对view的数据进行更改可能不会报错。这也是onCreate()
中不在主线程更新view
数据不会报错的缘由。
public void requestLayout() {
if (...) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
if (...) {
...
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
复制代码
View.requestLayout()
递归调用parent.requestLayout()
,最终递归到ViewRootImpl.requestLayout()
中,从新进行绘制,只会走measure
和layout
流程。
public void requestLayout() {
...
if (...) {
mParent.requestLayout();
}
}
复制代码
View.invalidate()
递归调用parent.invalidateChild()
,最终递归到ViewRootImpl.invalidateChild()
中从新绘制,只会走draw
流程。递归时传递了重绘的位置,只会对调用的view
的位置进行重绘。
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache, boolean fullInvalidate) {
...
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
p.invalidateChild(this, damage);
}
}
复制代码
ViewGroup.addView()
中前后调用了requestLayout()
和invalidate()
,从新进行绘制。前面说过在setContentView()
的时候,是经过addView()
进行添加,可是这个调用的时间在resume
以前,这个时候ViewRootImpl
尚未初始化,DecorView
的parent
还不是VIewRootImpl
,因此并不会开始绘制。
public void addView(View child, int index, ViewGroup.LayoutParams params) {
...
requestLayout();
invalidate(true);
addViewInner(child, index, params, false);
}
复制代码
绘制流程分红三个步骤:measure
,layout
,draw
。
measure
过程当中,会层层传递widthMeasureSpec
和heightMeasureSoec
。MesureSpec
是View
内的一个静态类,封装了父级传递到子级的布局要求,每一个MeasureSpec
表示了对高度宽度宽度的要求。使用32位int来表示,高2位表示测量模式,低30位表示测量尺寸。测量模式有三个:
USPECIFIED
:00,父布局不对子级施加任何约束。EXACTLY
:01,父布局已经肯定子级的大小,好比match_parent或者xxxdp。AT_MOST
:10,自适应,子级在父布局给定的范围内肯定本身的大小。MeasureSpec
中还封装了几个方法,方便对于MeasureSpec
的建立以及拆解。
public static class MeasureSpec {
public static int makeMeasureSpec(int size, int mode) {
...
return size + mode;
}
public static int getMode(int measureSpec) {
...
}
public static int getSize(int measureSpec) {
...
}
}
复制代码
DecorView.measure()
执行的是View.measure()
方法,能够看到这是个final
方法,也就是说全部view的measure都是走的这里,在标志了强制刷新(forceLayout
)或者尺寸并未被正确测量(needsLayout
)的状况下,会调用onMeasure()
进行测量。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...
if (forceLayout || needsLayout) {
...
onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
复制代码
对于不是ViewGroup
的View
,在onMeasure()
中只须要测量自身便可,通常会调用View.onMeasure(),在其中计算大小,并调用setMeasureDimension()
储存测量的宽高。
在getDefaultSize()计算宽高的时候,对于AT_MOST和EXACTLY都返回父类要求的最大大小,即wrap_content
和match_parent
取的计算结果都至关于match_parent
,若是直接继承View
实现自定义view的时候,须要注意处理这种状况。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = View.MeasureSpec.getMode(measureSpec);
int specSize = View.MeasureSpec.getSize(measureSpec);
switch (specMode) {
case View.MeasureSpec.UNSPECIFIED:
result = size;
break;
case View.MeasureSpec.AT_MOST:
case View.MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
复制代码
Decorview.onMeasure()
执行的是FrameLayout.onMeasure()
方法。主要的步骤是:
ViewGroup.meaureChildWithMargins()
测量子view大小,并取得子view中最大的宽高,做为自身可实现要求的最小宽高。resolveSizeAndState()
与父级传递的布局要求综合计算得到最终宽高。match_parent
的子view超过一个,会从新对这些view进行计算宽高,由于这些view宽高受到FrameLayout
的宽高的影响,以前的计算结果可能并不正确。protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (...) {
measureChildWithMargins();
final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
...
if (lp.width == FrameLayout.LayoutParams.MATCH_PARENT ||
lp.height == FrameLayout.LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
...
setMeasuredDimension(resolveSizeAndState(maxWidth, ...),
resolveSizeAndState(maxHeight, ...));
count = mMatchParentChildren.size();
if (count > 1) {
...
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
复制代码
ViewGroup.measureChildWithMargins()
,主要有两个工做
measure()
到这里就完成了一次完整的递归,又调用到了子view的measure()
,以后再继续根据view类型继续向下传递,完成整个view树的计算。
protected void measureChildWithMargins(...) {
...
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
复制代码
Decorview.layout()
执行的是ViewGroup.layout()
,主要调用的是super
的方法,也就是View.layout()
。
public final void layout(int l, int t, int r, int b) {
if (...) {
...
super.layout(l, t, r, b);
}
}
复制代码
View.onLayout()
首先调用setFrame()
,判断对其父view的位置是否发生改变,根据是否发生改变调用onLayout()
。
public void layout(int l, int t, int r, int b) {
...
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || ...) {
onLayout(changed, l, t, r, b);
...
}
}
private boolean setOpticalFrame(int left, int top, int right, int bottom) {
...
return setFrame(
left + parentInsets.left - childInsets.left,
top + parentInsets.top - childInsets.top,
right + parentInsets.left + childInsets.right,
bottom + parentInsets.top + childInsets.bottom);
}
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
...
}
return changed;
}
复制代码
onLayout()
主要完成对子view的从新布局,对于不是ViewGroup
的View
不包含子View,调用View.onLayout()
,是个空方法;而ViewGroup.onLayout()
是abstract
,须要不一样的布局管理器实现不一样的布局方法。
DecorView
会执行到FrameLayout.onlayout()
,计算出子view的位置信息,并调用子view的layout()
方法。
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
...
for (int i = 0; i < getChildCount(); i++) {
...
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
复制代码
Decorview
重写了draw()
方法,调用父类方法,也就是View.draw()
,以后作了菜单背景的绘制。
public void draw(Canvas canvas) {
super.draw(canvas);
if (mMenuBackground != null) {
mMenuBackground.draw(canvas);
}
}
复制代码
View.draw(),官方注释了绘制步骤:
其中2和5只在须要的状况下才会执行,主要的绘制流程是1,3,4。
public void draw(Canvas canvas) {
...
// Step 1, draw the background, if needed
if (!dirtyOpaque) {
drawBackground(canvas);
}
...
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
...
// Step 4, draw the children
dispatchDraw(canvas);
...
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
...
}
复制代码
View.drawBackgroud()
,背景不为空的状况下,最终调用Drawable.draw()
完成背景绘制。
private void drawBackground(Canvas canvas) {
final Drawable background = mBackground;
if (background == null) {
return;
}
...
background.draw(canvas);
}
复制代码
DecorView
也重写了ondraw()
,在其中也是先调用父类方法,而后绘制了自身,View.onDraw()
是个空方法,每一个view内容各不相同,须要对应的去实现各自的绘制方法,就好比DecorView.ondraw()
,在其中绘制后备背景,完成了自身的绘制。
public void onDraw(Canvas c) {
super.onDraw(c);
mBackgroundFallback.draw(this, mContentRoot, c, mWindow.mContentParent,
mStatusColorViewState.view, mNavigationColorViewState.view);
}
复制代码
DecorView.dispatchDraw()
执行的是ViewGroup.dispatchDraw()
,在其中遍历子view并调用drawChild()
方法。对于不是ViewGroup
的View
来讲,自己就没有子view,这个方法就没有什么意义,因此View.dispatchDraw()
是个空方法。
protected void dispatchDraw(Canvas canvas) {
...
for (int i = 0; i < childrenCount; i++) {
...
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || ...) {
more |= drawChild(canvas, child, drawingTime);
}
}
...
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
复制代码
ViewGroup.drawChild()
会调用到View中另外一个有boolean
返回值draw()
方法(View
中有两个draw()
方法),其中仍是会调用到没有返回值的draw()
方法。官方注释解释,这个有boolean
的draw()
方法,只是提供给ViewGroup.drawChild()
使用,这个方法的主要工做是根据图层类型(CPU
or GPU
)进行渲染和硬件加速的地方。
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
...
if (...) {
...
draw(canvas);
}
...
}
复制代码
到这里完成了draw
一次递归,最终完成整个树的绘制。