0%

Android View 绘制机制

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,在这里被赋值的。