View绘制整个流程
Activity走到onResume时,会调用ActivityThread的handleResumeActivity。
在这里会创建DecorView,通过WindowManager添加到PhoneWindow中。
这里也会创建ViewRootImpl,把DecorView的parent指定为ViewRootImpl。
再调用DecorView的requestLayout,requestLayout会层层的调用parent的requestLayout,最后走到ViewRootImpl的requestLayout。
然后走到ViewRootImpl.scheduleTraversals(),注册垂直同步监听。
当垂直同步信号来临时,去调用ViewRootImpl.doTraversal() .
再调用ViewRootImpl.performTraversals(),然后依次调用performMeasure()、performLayout()、performDraw()。
View.invalidate()也会层层调用parent的invalidateChildInParent,最后调用到ViewRootImpl的invalidateChildInParent,然后调用ViewRootImpl.scheduleTraversals()。
等待垂直同步信号来临时,调用ViewRootImpl.doTraversal(),由于没有给View设置FORCE_LAYOUT的flag,所以不会走measure和layout,只会performDraw(),并且只绘制dirty区域。
首次 View 的绘制流程是在什么时候触发的?
ActivityThread.handleResumeActivity 里触发的。
ActivityThread.handleResumeActivity 里会调用 wm.addView 来添加 DecorView,wm 是 WindowManagerImpl
最终通过 WindowManagerImpl.addView -> WindowManagerGlobal.addView -> ViewRootImpl.setView -> ViewRootImpl.requestLayout 就触发了第一次 View 的绘制。
ViewRootImpl 创建的时机?
ActivityThread.handleResumeActivity -> WindowManagerImpl.addView -> WindowManagerGlobal.addView 中创建ViewRootImpl
DecorView的创建时机?
ActivityThread.handleResumeActivity()中ActivityClientRecord.window.getDecorView();
PhoneWindow.getDecorView()调用installDecor() -> generateDecor()去new DecorView()
ViewRootImpl 和 DecorView 的关系是什么?
在 ViewRootImpl.setView 里,通过 DecorView.assignParent 把 ViewRootImpl 设置为 DecorView 的 parent。
所以 ViewRootImpl 和 DecorView 的关系就是 ViewRootImpl 是 DecorView 的 parent。
因为 DecorView 是我们布局的顶层,现在我们就知道层层调用 requestLayout 等方法是怎么调用到 ViewRootImpl 里的了。
Activity、PhoneWindow、DecorView、ViewRootImpl 的关系?
- PhoneWindow 其实是 Window 的唯一子类,是 Activity 和 View 交互系统的中间层。
- DecorView 是整个 View 层级的最顶层。
- ViewRootImpl 是 DecorView 的 parent,但是他并不是一个真正的 View,只是继承了 ViewParent 接口,用来掌管 View 的各种事件,包括 requestLayout、invalidate、dispatchInputEvent 等等。
如何触发重新绘制?
View的 requestLayout 和 invalidate
View.requestLayout()流程?
层层调用 parent 的 requestLayout ,一直到ViewRootImpl.requestLayout()
ViewRootImpl.requestLayout() 调用 scheduleTraversals() -> doTraversal() -> performTraversals() 开启绘制流程。
在 performTraversals 里,就是熟悉的 performMeasure -> performLayout -> performDraw 三个流程了。
在performDraw View 的绘制过程中,我们可以看到,只有 flag 被设置为 PFLAG_DIRTY_OPAQUE 才会进行绘制(这里划重点)。这也就是大家经常说的 requestLayout 不会引发 draw。
View.invalidate()流程?
invalidate -> invalidateInternal -> parent.invalidateChild
invalidateChild的while 循环里,会层层计算 parent 的 dirty 区域,最终会调用到 ViewRootImpl.invalidateChildInParent -> ViewRootImpl.invalidateRectOnScreen -> ViewRootImpl.scheduleTraversals -> ViewRootImpl.performDraw -> ViewRootImpl.draw -> DecorView的draw()
View.draw 方法,根据 PFLAG_DIRTY_OPAQUE flag 去决定是否重新绘制。
requestLayout 和 invalidate 的区别?
requestLayout 和 invalidate 都会触发整个绘制流程。但是在 measure 和 layout 过程中,只会对 flag 设置为 FORCE_LAYOUT 的情况进行重新测量和布局,而 draw 只会重绘 flag 为 dirty 的区域。
requestLayout 是用来设置 FORCE_LAYOUT 标志,invalidate 用来设置 dirty 标志。所以 requestLayout 只会触发 measure 和 layout,invalidate 只会触发 draw。
requestLayout 一定会触发onMeasure和onLayout吗?
不一定。
ViewRootImpl.performMeasure,最终调用的是 View.measure。
measureSpec 和 oldMeasureSpec 不相符的时候才会onMeasure。
ViewRootImpl.performLayout():
位置有变化才去onLayout。
PFLAG_DIRTY_OPAQUE是什么意思?
不透明,实心。
实心控件:控件的onDraw方法能够保证此控件的所有区域都会被其所绘制的内容完全覆盖。换句话说,通过此控件所属的区域无法看到此控件之下的内容,也就是既没有半透明也没有空缺的部分。
什么时候View的flag 被设置为 PFLAG_DIRTY_OPAQUE?
invalidate 会调用 parent.invalidateChild,在这里被赋值的。