收集整理 UI 原理解析

主要收集网上讲解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()这两个方法,因为上面两个方法已经完成了基本的事情。但是可以通过覆写在该基础之上做一些特殊的效果,比如

  1. @Override  
  2.     protected void dispatchDraw(Canvas canvas) {  
  3.         // TODO Auto-generated method stub  
  4.         //  
  5.         //可以在这里先做一些处理,包括对传入的canvas  
  6.         //  
  7.         super.dispatchDraw(canvas);         //这里会调用drawChild绘制子视图  
  8.         //  
  9.         //所有子视图都绘制完成后这里还可以做一些处理,比如绘制阴影什么的  
  10.         //  
  11.           
  12.     }  

其他

      从以上分析可以看出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读取数据,通讯就可以完成了。如下图:

 

clip_image002

 

    然而,在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的本质以图的形式展现就是如下:

    clip_image002[6]

  

   在服务端,多了个代理器:

    clip_image002[8]

   

 

   分析了CORBA的大体理论架构,下面我们来看一下Android的对象代理结构

    clip_image002[10]

 

在结构图中,我们可以较为清楚的把握Android的IPC包含了如下的概念:

  • 设备上下文什(ContextObject)

       设备上下文包含关于客服端,环境或者请求中没有作为参数传递个操作的上下文信息,应用程序开发者用ContextObject接口上定义的操作来创建和操作上下文。

  • Android代理:这个是指服务端在客户端的代理对象
  • Binder :Linux内核提供的Binder通讯机制(底层)
  • AIDL : Android 中的远程接口

Android的外特性空间是不需要知道服务在那里,只要通过代理对象完成请求。

 

 

2.窗口管理概述

   了解了Android 的基本架构原理,现在我们回到窗口管理这一块。

    Android的窗口管理是基于 C/S 模式的,客户端就是应用程序,服务端 就是 Window Manager服务。如下图:

 

    1

   

    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。他们之间的关系如下:

 

    2

   

    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 。如下图:

        3

    

    View 的成员变量 mParent 用来管理 View 上级关系。而 ViewGroup 里构建了焦点管理和子 View节点数组。这样通过 View 的mParent 和 ViewGroup的 mChildren 构建了 Android中View直接的关系网。如下图:

 

       4

 

 

  FocusPath

    所谓的 Foucs Path 就是我们的KeyEvent 传递的路线。一般的我们的KeyEvent 在主循环中,主View通过View的焦点记录关系传递到焦点 View。例如下图,View22是焦点,最顶层的View 通过 mFoucs的关系链找到最后形成的路径就是 Foucs Path。如下图红线所示:

 

      5

 

 

  ViewRoot

  • ViewRoot 实际上是一个 Handler,ViewRoot 简历主View 与 WindowsManager通讯的桥梁。
  • ViewRoot 本质上就是一个 Handler,我们知道 Handler 的基本功能 就是处理回调,发送消息。

    ViewRoot 通过 IWindowSession AIDL 添加窗口到 Window Manager ,而 IWindowAIDL 是Window Manager 分发消息给 Client ViewRoot 的渠道。

 

      6

 

 

  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简历会话上下文。如下图:

 

     7

 

 

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 的桥梁。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值