在介绍VIew绘制原理之前,简单介绍一下Window,ViewRootImpl,DecorView之间的联系。
一个 Activity 包含一个Window,Window是一个抽象基类,是 Activity 和整个 View 系统交互的接口,只有一个实现子类PhoneWindow,提供了一系列窗口的方法,比如设置背景,标题等。一个PhoneWindow 对应一个DecorView 跟 一个 ViewRootImpl,DecorView 是ViewTree 里面的顶层布局,是继承于FrameLayout,包含两个子View,一个id=statusBarBackground 的 View 和 LineaLayout,LineaLayout 里面包含 title 跟content,title就是平时用的TitleBar或者ActionBar, contenty也是FrameLayout,activity通过 setContent()加载布局的时候加载到这个View上。ViewRootImpl就是建立 DecorView 和 Window 之间的联系。
Window类是一个抽象类,提供绘制窗口的API
PhoneWindow是继承Window的一个具体的类,该类内部包含了一个DecorView对象,该DectorView对象是所有应用窗口(Activity界面)的根View
DecorView继承FrameLayout,里面id=content的就是我们传入的布局视图
有经验的同学肯定遇到过这样的场景:动态创建一个View之后,想要直接获取measureWidth 和 measureHeight往往取不到,这个时候我们会通过view.postDelayed()方法去获取。那么,问题来了,为什么这样就能取到呢?
Activity的onCreate()和onResum()方法里获取控件高度为空,因为还未测量。
Activity的onCreate()中setContentView中,new了DecorView。
View绘制入口
ActivityThread中,首先创建Activity,然后通过attach方法初始化对应的mWindow,然后将顶级视图DecorView添加到Windows中,并创建ViewRootImpl对象,这个对象就是沟通WindowManager和DecorView之间的桥梁,也是View绘制的开始。
View的绘制流程首先开始于ViewRootImpl的performTraversals()方法。依次经过三大过程,measure、layout、draw,performTraversals会依次调用performMeasure、performLayout、performDraw方法。其中measure用来对View进行测量,layout来确定子元素在父元素中的位置即真实宽高以及四个顶点位置,draw负责将View绘制出来。
setContentView流程
setContentView整个过程主要是如何把Activity的布局文件或者java的View添加至窗口里,重点概括为:
创建一个DecorView的对象mDecor,该mDecor对象将作为整个应用窗口的根视图。
依据Feature等style theme创建不同的窗口修饰布局文件,并且通过findViewById获取Activity布局文件该存放的地方(窗口修饰布局文件中id为content的FrameLayout)。
将Activity的布局文件添加至id为content的FrameLayout内。
当setContentView设置显示OK以后会回调Activity的onContentChanged方法。Activity的各种View的findViewById()方法等都可以放到该方法中,系统会帮忙回调。
参考链接:https://www.jianshu.com/p/5a71014e7b1b