主要收集网上讲解View的资料,感谢他们的贡献。
http://blog.csdn.net/windskier/article/details/6957854
Activity可以看做是整个android系统的人机接口,它提供了一个窗口来绘制UI,每个Activity在启动时,我们都需要给它设置一个Content view,作为Activity所呈现的UI内容,这个过程是通过setContentView()方法来实现的。
众所周知,android系统中强化了view的概念,主要是体现在对view的管理上,Android中的view以2种形态存在,单一的View和多个View组成的ViewGroup。Content view是以ViewGroup的形式存在的,也就是说在一个Activity窗口中可以添加多个View,这样就实现了Android窗口系统的UI多样化。activity启动时给activity窗口设置的Content view 是从xml文件中解析出来的,那么android是怎么样对这个ContentView进行管理的呢,它的内部实现逻辑又是怎样的呢?
在进行分析之前,首先给出一个Activity的window和view系统的层级关系,这个层级关系就是在Activity设置完ContentView之后的状况。
如下图。
下面来一一介绍各个层级的含义与作用
1.1 PhoneWindow
PhoneWindow是Android中的最基本的窗口系统,每个Activity 均会创建一个PhoneWindow对象,是Activity和整个View系统交互的接口。
1.2 DecorView
DecorView是当前Activity所有View的祖先,它并不会向用户呈现任何东西,它主要有如下几个功能,可能不全:
A. Dispatch ViewRoot分发来的key、touch、trackball等外部事件;
B. DecorView有一个直接的子View,我们称之为System Layout,这个View是从系统的Layout.xml中解析出的,它包含当前UI的风格,如是否带title、是否带process bar等。可以称这些属性为Window decorations。
C. 作为PhoneWindow与ViewRoot之间的桥梁,ViewRoot通过DecorView设置窗口属性。
1.3 System Layout
目前android根据用户需求预设了几种UI 风格,通过PhoneWindow通过解析预置的layout.xml来获得包含有不同Window decorations的layout,我们称之为System Layout,我们将这个System Layout添加到DecorView中,目前android提供了8种System Layout,如下图。
预设风格可以通过PhoneWindow方法requestFeature()来设置,需要注意的是这个方法需要在setContentView()方法调用之前调用。
1.4 Content Parent
Content Parent这个ViewGroup对象才是真真正正的ContentView的parent,我们的ContentView终于找到了寄主,它其实对应的是System Layout中的id为”content”的一个FrameLayout。这个FrameLayout对象包括的才是我们的Activity的layout(每个System Layout都会有这么一个id为”content”的一个FrameLayout)。
1.5 Activity Layout
这个ActivityLayout便是我们需要向窗口设置的ContentView,现在我们发现其实它的地位很低,同时这一部分才是和user交互的UI部分,其上的几层并不能响应并完成user输入所期望达到的目的。
Android系统的视图结构的设计也采用了组合模式,即View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类,由此就得到了视图部分的基本结构--树形结构
View定义了绘图的基本操作
基本操作由三个函数完成:measure()、layout()、draw(),其内部又分别包含了onMeasure()、onLayout()、onDraw()三个子方法。具体操作如下:
1、measure操作
measure操作主要用于计算视图的大小,即视图的宽度和长度。在view中定义为final类型,要求子类不能修改。measure()函数中又会调用下面的函数:
(1)onMeasure(),视图大小的将在这里最终确定,也就是说measure只是对onMeasure的一个包装,子类可以覆写onMeasure()方法实现自己的计算视图大小的方式,并通过setMeasuredDimension(width, height)保存计算结果。
2、layout操作
layout操作用于设置视图在屏幕中显示的位置。在view中定义为final类型,要求子类不能修改。layout()函数中有两个基本操作:
(1)setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,该函数用于将这些参数保存起来;
(2)onLayout(),在View中这个函数什么都不会做,提供该函数主要是为viewGroup类型布局子视图用的;
3、draw操作
draw操作利用前两部得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。子类也不应该修改该方法,因为其内部定义了绘图的基本操作:
(1)绘制背景;
(2)如果要视图显示渐变框,这里会做一些准备工作;
(3)绘制视图本身,即调用onDraw()函数。在view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的显示(比如TextView在这里实现了绘制文字的过程)。而对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的,其包含了多个子view,而子View已经实现了自己的绘制方法,因此只需要告诉子view绘制自己就可以了,也就是下面的dispatchDraw()方法;
(4)绘制子视图,即dispatchDraw()函数。在view中这是个空函数,具体的视图不需要实现该方法,它是专门为容器类准备的,也就是容器类必须实现该方法;
(5)如果需要(应用程序调用了setVerticalFadingEdge或者setHorizontalFadingEdge),开始绘制渐变框;
(6)绘制滚动条;
从上面可以看出自定义View需要最少覆写onMeasure()和onDraw()两个方法。
ViewGroup中的扩展操作:
首先Viewgroup是一个抽象类。
1、对子视图的measure过程
(1)measureChildren(),内部使用一个for循环对子视图进行遍历,分别调用子视图的measure()方法;
(2)measureChild(),为指定的子视图measure,会被 measureChildren调用;
(3)measureChildWithMargins(),为指定子视图考虑了margin和padding的measure;
以上三个方法是ViewGroup提供的3个对子view进行测量的参考方法,设计者需要在实际中首先覆写onMeasure(),之后再对子view进行遍历measure,这时候就可以使用以上三个方法,当然也可以自定义方法进行遍历。
2、对子视图的layout过程
在ViewGroup中onLayout()被定义为abstract类型,也就是具体的容器必须实现此方法来安排子视图的布局位置,实现中主要考虑的是视图的大小及视图间的相对位置关系,如gravity、layout_gravity。
3、对子视图的draw过程
(1)dispatchDraw(),该方法用于对子视图进行遍历然后分别让子视图分别draw,方法内部会首先处理布局动画(也就是说布局动画是在这里处理的),如果有布局动画则会为每个子视图产生一个绘制时间,之后再有一个for循环对子视图进行遍历,来调用子视图的draw方法(实际为下边的drawChild());
(2)drawChild(),该方法用于具体调用子视图的draw方法,内部首先会处理视图动画(也就是说视图动画是在这里处理的),之后调用子视图的draw()。
从上面分析可以看出自定义viewGroup的时候需要最少覆写onMeasure()和onLayout()方法,其中onMeasure方法中可以直接调用measureChildren等已有的方法,而onLayout方法就需要设计者进行完整的定义;一般不需要覆写以dispatchDraw()和drawChild()这两个方法,因为上面两个方法已经完成了基本的事情。但是可以通过覆写在该基础之上做一些特殊的效果,比如
- @Override
- protected void dispatchDraw(Canvas canvas) {
- // TODO Auto-generated method stub
- //
- //可以在这里先做一些处理,包括对传入的canvas
- //
- super.dispatchDraw(canvas); //这里会调用drawChild绘制子视图
- //
- //所有子视图都绘制完成后这里还可以做一些处理,比如绘制阴影什么的
- //
- }
其他
从以上分析可以看出View树的绘制是一个递归的过程,从ViewGroup一直向下遍历,直到所有的子view都完成绘制,那这一切的源头在什么地方(是谁最发起measure、layout和draw的)?当然就是在View树的源头了——ViewRoot!,ViewRoot中包含了窗口的总容器DecorView,ViewRoot中的performTraversal()方法会依次调用decorView的measure、layout、draw方法,从而完成view树的绘制。
invalidate()方法
invalidate()方法会导致View树的重新绘制,而且view中的状态标志mPrivateFlags中有一个关于当前视图是否需要重绘的标志位DRAWN,也就是说只有标志位DRAWN置位的视图才需要进行重绘。当视图调用invalidate()方法时,首先会将当前视图的DRAWN标志置位,之后有一个循环调用parent.invalidateChildinParent(),这样会导致从当前视图依次向上遍历直到根视图ViewRoot,这个过程会将需要重绘的视图标记DRAWN置位,之后ViewRoot调用performTraversals()方法,完成视图的绘制过程。
在学习 WindowManager 接口的时候,了解到这个接口很重要,因为它可以直接与WindowManager(窗口管理器)进行交互,那这个 Window Manager 究竟是怎么一回事呢?
通过查找资料,我知道了 Window Manager 其实是一个service(服务)。它是全局的,系统中唯一的,是独立于android应用程序的,所有android应用程序公用的一个单独的C++服务。(这个“单独的C++服务”的说法,建议先看看:android概念空间及seviceManager的介绍)
1. 基本架构原理
要了解这个服务,我们首先要对android的基本架构原理有个了解。
Android是基于 C/S 模式的。在我们的根深蒂固的想法中,C/S架构就是客户端和服务端直接通过Binder交互数据,打开Binder写入数据,通过Binder读取数据,通讯就可以完成了。如下图:
然而,在Android 的概念中,Binder 是一个很低层的概念,是 Linux 内核提供的 Binder 通讯机制。上面一层根本都看不到Binder ,而是 Activity 跟一个 Service 的对象直接通过方法调用,获取服务。
这个就是Android提供给我们的外特性:在Android中,要完成某个操作,所需要做的就是请求某个有能力的服务对象去完成动作,而无需知道这个通讯是怎样工作的,以及服务在哪里。所以Andoid 的 IPC(进程间通信)在本质上属于对象请求代理架构(对象请求代理架构:CORBA)。就是说它不仅解决了进程间的通信问题,还是一个架构,提出了一种设计理念。
既然android的IPC 的本质是CORBA,那么我们就来分析一下CORBA。
【CORBA】
由OMG组织制订的一种标准的面向对象应用程序体系规范,或者说CORBA体系结构是对象管理组织(OMG)为解决分布式处理环境(DCE)中,硬件和软件系统的互连而提出的一种解决方案。
CORBA定议了一系列API,通信协议,和 物件/服务信息模型用于使得异质应用程序能够互相操作,这些应用程序用不同的程序语言编写,运行在不同的平台上。CORBA因此为定义明确的物件提供了平台和位置的透明性,这些物件是分布式计算平台的基础。
COBRA标准主要分为3个层次:对象请求代理、公共对象服务和公共设施。最底层最核心就是ORB(对象请求代理)。
ORB规定了分布对象的定义(接口)和语言映射,实现对象间的通讯和互操作,是分布对象系统中的"软总线";在ORB之上定义了很多公共服务,可以提供诸如并发服务、名字服务、事务(交易)服务、安全服务等各种各样的服务;最上层的公共设施则定义了组件框架,提供可直接为业务对象使用的服务,规定业务对象有效协作所需的协定规则
【ORB】
ORB是一个在对象间建立客户/服务器联系的中件。使用ORB,客户可以调用服务器的对象或对象中的应用,被调用的对象不要求在同一台机器上。由ORB负责进行通信,同时ORB也会在调用对象完成后返回结果。客户对象完全可以不关心服务器对象的位置,实现它所采用的具体技术和工作的硬件平台,甚至不必关心服务器对象的与服务无关的接口信息,这就大大简化了客户程序的工作。既然能够这么方便,那ORB就需要提供在不同机器间应用程序间的通信,数据转换,并提供多对象系统的无缝连接。
CORBA的本质以图的形式展现就是如下:
在服务端,多了个代理器:
分析了CORBA的大体理论架构,下面我们来看一下Android的对象代理结构:
在结构图中,我们可以较为清楚的把握Android的IPC包含了如下的概念:
- 设备上下文什(ContextObject)
设备上下文包含关于客服端,环境或者请求中没有作为参数传递个操作的上下文信息,应用程序开发者用ContextObject接口上定义的操作来创建和操作上下文。
- Android代理:这个是指服务端在客户端的代理对象
- Binder :Linux内核提供的Binder通讯机制(底层)
- AIDL : Android 中的远程接口
Android的外特性空间是不需要知道服务在那里,只要通过代理对象完成请求。
2.窗口管理概述
了解了Android 的基本架构原理,现在我们回到窗口管理这一块。
Android的窗口管理是基于 C/S 模式的,客户端就是应用程序,服务端 就是 Window Manager服务。如下图:
Activity建立一个主窗口之后,在将主窗口添加到 Window Manager 时,首先要建立 WindowManager的代理对象,并打开一个Session(session:会话;实现 IWindowSession AIDL接口),并维持该会话(Activity 将通过该会话与 Window Manager 建立联系,这个Session 是C/S体系的基础)。Client 通过这个Session 将 window 加入到 Window Manager 中。
一个完整的窗口概念包含了 View,ViewRoot,Window Manager Service,Window,DecorView,IWindow,ISession,WindowState。他们之间的关系如下:
Client 端的Activity 通过 IWindowSession 会话与Window Manager Service 建立对话,而 WindowManager Service 通过 IWindow 接口访问 Client,将消息传递到Client端,在通过消息分发渠道,将消息传递到具体的消息处理函数。(用户输入等操作最先是到窗口管理服务,由窗口管理服务发给活动窗口,再一步步传递到焦点)。
3. Client 端
客户端组成:
Window,View,ViewRoot,Window Manager Proxy
View:
View 在 Android 中包含了 交互 和显示。
在Activity 在 performLaunchActivity 时,会使用Activity.attach() 建立一个PhoneWindow 主窗口。这个主窗口的建立并不是一个重点。handleResumeActivity真正要启动一个Activity 的时候,会将主窗口加入到 Window Manager,当然并不是主窗口本身,而是指的主窗口的DecorView 。
DecorView :实际上是一个ViewGroup ,在依存关系上来讲,对看这主窗口来讲,DecorView 是 Top-LevelView 。如下图:
View 的成员变量 mParent 用来管理 View 上级关系。而 ViewGroup 里构建了焦点管理和子 View节点数组。这样通过 View 的mParent 和 ViewGroup的 mChildren 构建了 Android中View直接的关系网。如下图:
FocusPath
所谓的 Foucs Path 就是我们的KeyEvent 传递的路线。一般的我们的KeyEvent 在主循环中,主View通过View的焦点记录关系传递到焦点 View。例如下图,View22是焦点,最顶层的View 通过 mFoucs的关系链找到最后形成的路径就是 Foucs Path。如下图红线所示:
ViewRoot
- ViewRoot 实际上是一个 Handler,ViewRoot 简历主View 与 WindowsManager通讯的桥梁。
- ViewRoot 本质上就是一个 Handler,我们知道 Handler 的基本功能 就是处理回调,发送消息。
ViewRoot 通过 IWindowSession AIDL 添加窗口到 Window Manager ,而 IWindowAIDL 是Window Manager 分发消息给 Client ViewRoot 的渠道。
Window ManagerProxy
Activity 在使用 getSystemService 获取 WindowManagerImpl 时,建立了一个WindowManagerImpl 的实例,这就是 Window Manager服务 的代理。
wm=(WindowManagerImpl)context.getSystemService(Context.WINDOW_SERVICE);
并调用 wm.addview 添加窗口到 WMService 中。
在这个过程中客户端建立了什么样的管理框架,并如何这个会话?
在 Window Manager Proxy 中建立了 View,Layout,ViewRoot散着的对应关系表。构造一个ViewRoot 就会打开一个Session,并利用 IWindowSession简历会话上下文。如下图:
4. 服务端 ---- Window ManagerService
WindowManager Service 管理的窗口是应用程序的 Top-Level 窗口,这里称之为 主窗口,为什么主窗口要放到Service 来管理呢?其实 放在一起管理是为了计算 Z-order 序列,根据应用程序的状态来显示/隐藏应用程序的窗口。
在 Service维护了一个 mWindow 数组,这个 mWindow 就是 Window 的 Z-order序 数组。mWindowMap 用于记录<Client:Binder,WindowState对象>。
在服务端的窗口对象叫做 WindowState 。WindowState 有一个叫做 mClient 成员变量来记录客户端IWindow 实例,通过 IWindow 接口实例,Service可以访问客户端的信息,可以传递消息到客户端。所以说呢IWindow 是 Service 链接 View 的桥梁。