上一篇我们学习了 android 的一些基本控件,这些控件都有一个共同基类 View,也就是说他们都是 view ,在 android 知识体系中,view 扮演着很重要的角色,是 android 的视觉呈现。所以我们有必要来理清 View 的工作原理与事件原理。(注:读《Android 第一行代码》 和 《Android 开发艺术探索》的个人读书笔记)
我们先来看一幅图:
这幅图描述的 view 的家族结构,其中 ViewGroup 中可以包含有子 view,我们在打开一个 activity ,主要是与其中的视图进行交互,经常困扰我的是这些视图是这样被显示到屏幕的,由于 view 族是树状结构的,我们理解一个 acvitity 中的视图如何被显示出来,主要理解最顶级的 view,如何被显示出来即可。
1.认识 DecorView
DecorView 是一个垂直方向的 LinearLayout,包含有一个状态栏、一个内容区和一个导航栏,其中内容区包含有一个标题栏和一个内容栏。(不同的主题或设定而有所不同,不过大致一样)其中内容栏对应一个 ContentFramLayout 布局,其 id 为 content,这个 ContentFramLayout 就是我们 activity 对应的根布局的父容器了,在 activity 中的通过 setContentView 方法,把 activity 对应的布局文件或者 view 加载到 ContentFramLayout 中, 这样就把我认为的顶级 view 加载到内容栏中,可以把这个 view 叫做 contentView 吧。
看下图可以更易理清 DecorView 与 ContentView 的关系:
大家可以看到上图中状态栏、标题栏、内容栏、导航栏的对应关系,其中整个界面就是一个 LinearLayout(DecorView),现在我们需要弄清楚的是DecorView 与 ContentView 的关系:
我们可以这样获得 contentView 呢:
ViewGroup content = findViewById(R.id.content);
View contentView = content.getChildAt(0);
(注意:DecorView 才是 ViewTree 的根布局,是真正的顶级 view)
知道了 DecorView 的存在,现在要弄清楚视图是怎样显示到屏幕上的,就等价于把怎样把 DecorView 绘制显示在屏幕上。绘制显示,从字面上我们可以想到的是基本算法流程就是:测量绘制区域的大小->确定绘制的位置->绘制。
2.认识 ViewRoot
大家一看到 ViewRoot ,都会以为这是 ViewTree 的根 view (DecorView 才是 ViewTree 的根布局),我也是这样觉得的,但是 ViewRoot 和 ViewTree,并没有直接的联系。
ViewRoot 有一个对应的类 ViewRootImpl,就看 Impl 我以为这是 ViewRoot 的一个实现类,但是当我尝试去查看源码时发现并不是我所理解的那种实现类,觉得就是那种功能上的实现吧。(作为一个菜鸟,看不懂 the fucking code) android 低版本中使用 ViewRoot,高版本中使用 ViewRootImpl 来完成它的功能的。它实现了 View 和 WindowManager 联系的规则,是 View 和 WindowManager 的纽带,view 的三大动作流程(测量,确定位置,绘制)均是通过 ViewRoot 来完成。 现在我只知道它是用来辅助 view 的绘制显示的。
3.认识 Window
源码中是这样介绍的:
Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.The only existing implementation of this abstract class is android.policy.PhoneWindow, which you should instantiate when needing a Window. Eventually that class will be refactored and a factory method added for creating Window instances without knowing about a particular implementation.
翻译过来:
它是顶级窗口外观和行为策略描述的抽象基类,应该使用这个类的实例作为添加到窗口管理器的顶级视图,它提供标准的UI策略,如背景,标题区域,默认密钥处理等。这个抽象类的唯一现有的实现类是android.policy.PhoneWindow,您需要在需要窗口时实例化。最终,该类将被重构,并添加了一种工厂方法来创建 Window 实例,而不用了解特定的实现。
我的理解就是,视图是依赖 window 来显示的,有视图就有相应的 Window,Window是通过view的形式存在的。在 android 中有三类 window 分别是:系统 Window、子 Window、应用类 Window
类型 | 等级 | 对应例子 |
---|---|---|
系统 Window | 2000-2999 | 悬浮窗口、toast |
子 Window | 1000-1999 | dialog、popupwindow、menu |
应用类 Window | 1-99 | activity |
现在主要是学习应用类 Window,在 ActivityThead 中,activity 被创建后,会创建一个 decorview 添加到 window 中(decorview 是 window 对象中的一个成员变量 ),同时也会创建一个 ViewRoootImpl 对象,这样就建立起 decorview 与 window 的联系了。Decorview、ViewRoootImpl 、Window 就是 android 视图在屏幕上显示所需要的一些类对象了。
Window 是一个抽象的概念,Window 并不是真实存在的(注意这里是指可视窗体,并不是指 window 对象),是通过 view 的形式存在的,一个 window 对应着一个顶级 view,由于 Window 是一个抽象的概念,所以是用 WindowManger 来管理窗体的,也就是说管理 view 。实际上 WindowManger 通过持有 ArrayList< View > 和ArrayList< ViewRootImpl > 来管理 View 的(当然这里不是真正由 WindowManger 持有,可以这样简单认为),这里的一个 view 就代表着一个 Window 窗口了。我们在做一些悬浮窗口的效果,就是使用 WindowManger 来操纵窗体的,换句话说就是操纵 要显示的 view。
我们可以这样获取 WindowManger:
WindowManger windowManger = (WindowManger)getSystemService(Context.WINDOW_SERVICE);
WindowManger 的三个重要方法:
//添加一个 view
addView(view,layoutParams);
//更新一个 view
updateViewLayout(view,layoutParams);
//移除一个 view
removeView(view)
从 WindowManger 中的方法中可以看出,WindowManger 是直接操纵 view 的,而不是操纵 window 对象,不难理解上面所说的 Window 是一个抽象的概念,Window 并不是真实存在的,是通过 view 的形式存在的,这一说法。
WindowManger 的一个重要参数:LayoutParams,储存了 view 相对于 屏幕的位置,view 的大小,view 的类型(window的类型)等属性,其中有两个比较重要的属性,分别是flag 和 type。
flag取值 | 说明 |
---|---|
FLAG_NOT_FOUCUSABLE | 表示window不要获取焦点,即自己不处理事件,最终会把事件传给具有焦点的下层window |
FLAG_BOT_TOUCH_MODAL | window区域以内的事件自己处理,window以外的区域交给下层window处理 |
FLAG_SHOW_WHEN_LOCKED | 开启此模式可以让window显示在锁屏的界面 |
type:指定 window 的类型,可以是上面所说的三种类型之一。
小结:回到问题本身,一个 acvitity 中的视图如何被显示出来,现在我可以认为是, acvitity 对应的视图被加载到 DecorView 中,然后通过 ViewRootImpl 完成对 View(包括所有子 view) 的测量,确定位置,绘制三个流程,并在 WindowManager 的管理下被显示在屏幕中。
模模糊糊学习了 android 中视图是则怎么样被组织显示出来的,接下来我需要关注的是如何构造自己的 ContentView。包括: android 中的坐标系、常用几大布局、View 的事件原理、自定义 View、自定义 ViewGroup。
(注:以上为看《 Android 开发艺术探索》的读书笔记与理解。)