一、View绘制基本流程
简单的可以说,如 measure,layout,draw 分别对应测量,布局,绘制三个过程。
- ① measure:测量。系统会先根据xml布局文件和代码中对控件属性的设置,来获取或者计算出每个View和ViewGrop的尺寸,并将这些尺寸保存下来。
- ② layout:布局。根据测量出的结果以及对应的参数,来确定每一个控件应该显示的位置。
- ③ draw:绘制。确定好位置后,就将这些控件绘制到屏幕上。
总体流程来说Android 的绘制基本可以分为
- ①* View 的绘制 --> 也就是ViewrootImpl,在这姑且先将它理解为Activity
- ② Viewgroup的绘制
- ③ View 的绘制
自定义View的时候一般需要重写父类的onMeasure()、onLayout()、onDraw()三个方法,来完成视图的展示过程。
二、Android视图层次结构简介
Activity内部有个Window成员,它的实例为PhoneWindow,PhoneWindow有个内部类是DecorView,这个DecorView就是存放布局文件的,里面有TitleActionBar和我们setContentView传入进去的layout布局文件
Window类:是一个抽象类,提供绘制窗口的API。
PhoneWindow:是继承Window的一个具体的类,该类内部包含了一个DecorView对象,该DectorView对象是所有应用窗口(Activity界面)的根View。
DecorView:继承FrameLayout,里面id=content的就是我们传入的布局视图,它可以被认为是 Android 视图树的根节点视图。
AndroidStudio工具中提供了一个布局视察器工具,通过Tools > Android > Layout> Inspector可以查看具体某个Activity的布局情况。上图中,左边树状结构对应了右边的可视图,可见DecorView是整个界面的根视图,对应右边的红色框,是整个屏幕的大小。黄色边框为状态栏部分;那个绿色边框中有两个部分,一个是白框中的ActionBar,对应了上图中紫色部分的TitleActionBar部分,即标题栏,平时咱们可以在Activity中将其隐藏掉;另外一个蓝色边框部分,对应上图中最里面的蓝色部分,即ContentView部分。下图中左边有两个蓝色框,上面那个中有个“contain_layout”,这个就是Activity中setContentView中设置的layout.xml布局文件中的最外层父布局,咱们能通过layout布局文件直接完全操控的也就是这一块,当其被add到视图系统中时,会被系统裹上ContentFrameLayout(显然是FrameLayout的子类),这也就是为什么添加layout.xml视图的方法叫setContentView(…)而不叫setView(…)的原因。
三、相关问题点
1、什么是 Activity?从视图角度分析
- Activity 并不负责视图控制,它只是控制生命周期和处理事件。真正控制视图的是Window。一个Activity 包含了一个Window,Window才是真正代表一个窗口。
- Activity就像一个控制器,统筹视图的添加与显示,以及通过其他回调方法,来与Window、以及View 进行交互。
2、什么是Window?
-
Window 是一个抽象类,实际在Activity中持有的是其子类PhoneWindow。PhoneWindow中有个内部类DecorView,通过创建DecorView 来加载Activity中设置的布局R.layout.activity_main
-
Window 是视图的承载器,内部持有一个DecorView,而这个DecorView才是view 的根布局。
-
Window 通过WindowManager 将DecorView 加载其中,并将DecorView 交给ViewRootimpl,进行视图绘制以及其他交互
3、什么是DecorView?
-
DecorView 是 FrameLayout 的子类,它可以被认为是 Android 视图树的根节点视图。
-
DecorView 作为* View,一般情况下它内部包含一个竖直方向的 LinearLayout,在这个 LinearLayout 里面有上下三个部分,上面是个 ViewStub,延迟加载的视图(应该是设置ActionBar,根据 Theme 设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。具体情况和Android版本及主体有关。
-
在 Activity 中通过 setContentView 所设置的布局文件其实就是被加到内容栏之中的,成为其唯一子 View,就是上面的 id 为 content 的 FrameLayout 中,在代码中可以通过 content 来得到对应加载的布局。
4、什么是 ViewRootImpl
从结构上来看,ViewRootImpl 和 ViewGroup 其实是一种东西。
相同点
- 它们都继承了ViewParent。
- ViewParent 是一个接口,定义了一些父View 的基本行为,比如requestlayout,getparent 等。
不同点
- ViewRootImpl 并不会像ViewGroup 一样被真正绘制在屏幕上
- 在Activity 中,它是专门用来绘制DecorView 的,核心方法是setView
5、Activity,window,View 三者之间的关系是什么?
window 是 activity 的一个成员变量,window 和 View 是“显示器”和“显示内容”的关系
-
Window 抽象类,PhoneWindow 唯一实现类,用于加载Activity 的*View –DecorView
-
一个 Activity 对应一个 Window 也就是 PhoneWindow,一个 PhoneWindow 持有一个 DecorView 的实例,DecorView 本身是一个 FrameLayout
6、Activity、PhoneWindow、DecorView 关系图
类似的问题,其实问的基本是一个东西。activity 的 setContentView 方法实际上是就是交给 phonewindow 去做的。window 和 View 的关系可以类比为显示器和显示的内容。
7、Android 中 xml 布局怎么显示到屏幕上的?
-
在整个 Activity 的生命周期中,setContentView 是在 onCreate 中调用的,它实现了对资源文件的解析,完成了 xml 文件到 View 的转化。
-
那么 View 真正开始绘制是在哪个生命周期呢?
答案是 onResume 结束后 -
onResume 之后,从 Activity 中的 Window 实例中获取Decorview。
调用activity 中windowmanager 的addView 方法,将decorView 传入到ViewRootImpl 的setView 方法中通过ViewRootImpl.setView() 来完成View 的绘制