draw2d设计内幕之一:LightweightSystem设计和实现剖析
作者:余学锋 编辑:javamxj 发布:javamxj 源站点:分享Java快乐
这篇文章假设读者在浏览本章之前,已经对LightweightSystem有了一定的了解,基本上应该要明白LightweightSystem究竟是个什么?在 分享Java快乐 的 Draw2d专栏 中有关于LightweightSystem介绍的文章。在本文章中,提供了一个dxf文件解析器和dxf浏览器。dxf浏览器就是利用draw2d编写的。本文章力图言简意赅的解释与LightweightSystem相关的设计和实现,所以对一些基本的技术名词和术语并没有或没有详细的解释。
1. LightweightSystem设计和实现
1.1 LightweightSystem设计
LightWeightSystem设计图
设计图表明,一个LightweightSystem由四个不可缺少的部分组成:画布控件、图形元素更新管理器、事件分发器、根图形元素。内容是需要在画布上显示的与应用相关的图新元素,并不要求LightweightSystem一定就要拥有内容图形元素;对LightweightSystem而言,内容图形元素可有可无,但如果内容图形元素为空,那么LightweightSystem就什么也不显示。向LightweightSystem设置内容的动作,实际上就是将内容图形元素作为孩子增加到根图形元素中,并且在LightweightSystem中记录下对内容图形元素的直接引用(由变量contents引用)。
UpdateManager是一个抽象类,它为具体的更新管理器提供基本实现。DeferredUpdateManager是UpdateManager类的具体实现,它以异步更新图形元素的方式执行更新过程,这种方式在许多软件中,被叫做多线程呈现;如果读者有MFC或win32开发经验,比较容易了解DeferredUpdateManager的实现原理了:在MFC或Win32中,如果要使某个区域无效,需要调用Invalidate通知系统某个区域无效,在系统接到这个通知后,并不是立即去更新无效区域而是将无效区域放入到系统保持的一个无效区域列表中,以后在程序空闲时再一并处理无效区域列表中的所有脏区域(取它们的并集),所以有时即使在程序中调用许多次invalidate,但并不会显著影响系统的呈现效率,原因就是如此。当使用DeferredUpdateManager管理图形元素的更新时,图形元素的更新总是要延迟于使图形元素无效的动作,例如,如果用户用鼠标resize化应用程序窗口,就会发现窗口在被resize后,在靠近被鼠标一侧的窗口区域有一块灰色区域,但随即该区域就被正常的被填充上了;这块灰色区域就是用户resize窗口过程中产生的脏区域,在拖动的过程中,应用程序已经向DeferredUpdateManager报告了有脏区域产生的事实,但DeferredUpdateManager仅仅只是创建一个线程,但因为涉及到更新UI的问题,该线程必须在UI线程中被执行;但此时UI主线程正忙于处理用户的resize动作,所以不会执行DeferredUpdateManager的更新请求;无论用户以多快的速度执行resize动作,在两次resize过程中总会有空隙,在这个空隙中,UI主线程就会执行DeferredUpdateManager的更新请求。异步更新图形元素的方式并不会提高图形元素呈现的速度,但它会极大的改善UI界面的响应能力。
UpdateManager持有根图形元素的应用,因为只有通过根图形元素,才能够获取待更新的图形元素,请参见对根图形元素作用的描述。
EventDispatcher是一个抽象类,它为具体的事件分发器提供基本实现。SWTEventDispatcher是EventDispatcher类的具体实现。顾名思义,SWTEventDispatcher与SWT控件套件有关。draw2d的SDK文档是这样描述SWTEventDispatcher的:SWTEventDispatcher使draw2d具有分发SWT事件的能力;LightweightSystem增加SWT事件监听器到它的画布控件;当画布控件接受到一个SWT事件后,它就调用SWTEventDispatcher中定义的一个合适的分发器方法。在设计良好的图形系统中,自定义一套于平台无关的事件处理机制是一种通行的做法。Draw2d也不例外,在Draw2d中定义了与操作系统无关的事件序列,SWTEventDispatcher的主要职责之一就是要将来自于SWT的事件转换层draw2d中定义的内部事件,然后再将内部事件分发给恰当的图形元素。
可以LightweightSystem看成一个Canvas的适配器,它扩展了canvas的功能,并完全隐藏了与canvas的通信过程。可以直接将LightweightSystem看成一个拥有绘图表面、能够管理图形元素的绘制并处理各种用户交互事件的控件。
1.2 LightweightSystem实现
在LightweightSystem中定义了如下的变量:
private Canvas canvas; //充当绘图表面
IFigure contents; //需要在Canvas中显示的图形元素
private IFigure root; //LightweightSystem中的根元素
private EventDispatcher dispatcher; //事件分配器
private UpdateManager manager = new DeferredUpdateManager(); //更新管理器
private Rectangle oldControlSize = new Rectangle(); //画布控件的旧大小
除root外,其它几个图形元素外都比较容易理解。在前几篇文章中,已经对root的作用进行了描述,但为了保持文章的完整性,这里在重复一下对它的描述。根图形的职责如下:
1. 作为应用程序的背景。根图形元素完全覆盖在画布上,用户可以设置根图形元素的显示属性,改变根图形元素的显示属性就可以改变应用程序的背景。
2. 作为内容图形元素的父亲。
3. 作为搜索某个图形元素的搜索入口点。
4. 具备布局属性。因为它是内容图形元素的父亲,所以可以对内容图形元素进行布局。在当前的LightWeightSystem实现中,根图形元素的默认布局是堆叠布局(StackLayout)。
5. 通过绘制根图形元素中某个区域,可以绘制位置落在这个区域之内所有的图形元素。
应用程序不要期望重新定义LightWeightSystem中的根图形元素,这是不允许的。
实际上,从1.1节中的LightWeightSystem中的设计图中,比较容易理解LightWeightSystem为什么要定义这些变量。在某些可将设计转换成代码的CASE工具中,可以直接生成这些变量定义。
从LightWeightSystem的初始化过程中,可以对LightWeightSystem的逻辑结构有一个比较好的理解,
· LightWeightSystem的初始化序列如下:
public LightWeightSystem(Canvas c)
{
//首先创建根图形元素并用该根图形元素初始化更新管理器和LightweightSystem对象的根图形元素。
RootFigure tmpRoot = createRootFigure();
GetUpdateManager().SetRoot( tmpRoot );
This.root = tmpRoot;
//更新管理器要更新图形元素时,需要一个具体的绘图上下文。为getUpdateManager设置图形上下文来源的目的就是
//告知更新管理器,当其执行更新时,如何获取图形上下文。通常,图形上下文是从控件中获取的。
canvas = c;
getUpdateManager().setGraphicsSource(new BufferedGraphicsSource(canvas));
//根据canvas的大小调整root图形元素的约束范围(使root与canvas的客户区一样大)
controlResized();
//接下来将LightweightSystem作为监听器注册到Canvas
addListeners();
}
· controlResized()方法方法体如下:
{
Rectangle r = new Rectangle(canvas.getClientArea());
r.setLocation(0, 0);
root.setBounds(r); //重新设置根图形元素的范围,使其大小等于画布控件的客户区大小,并完全覆盖在画布之上。
//使根图新元素重新有效,该调用会使根图新元素重新布局内容图形元素。
root.revalidate();
//通知更新管理器,更新根图形元素。
manager.performUpdate();
oldControlSize = r; //保存老的控件大小。
}
· addListeners()方法要负责将LightweightSystem对象作为监听器注册到Canvas中,该方法方法体如下:
protected void addListeners()
{
//首先创建一个事件处理器,这个事件处理器实现了许多的接口,这些接口中方法负责接受SWT的各种交互事件。
EventHandler handler = createEventHandler();
//处理辅助交互事件(为残疾人准备的)
canvas.getAccessible().addAccessibleListener(handler);
canvas.getAccessible().addAccessibleControlListener(handler);
//处理鼠标按下
canvas.addMouseListener(handler);
//处理鼠标移动等事件。
canvas.addMouseMoveListener(handler);
//处理鼠标跟踪事件。
canvas.addMouseTrackListener(handler);
//处理键盘事件。
canvas.addKeyListener(handler);
//处理遍历事件(模拟控件被遍历的情况)
canvas.addTraverseListener(handler);
//处理聚焦事件。
canvas.addFocusListener(handler);
//重新设置根图形元素的约束范围。
root.setBounds(oldControlSize);
//通知更新管理器,更新根图形元素。
getUpdateManager().performUpdate();
//初始化事件分发器。
setEventDispatcher(getEventDispatcher());
}
//GetEventDispatcher()函数体
protected EventDispatcher getEventDispatcher()
{
if (dispatcher == null)
dispatcher = new SWTEventDispatcher();
return dispatcher;
}
· EventHandler类(事件处理器)的定义
protected class EventHandler implements MouseMoveListener, MouseListener, AccessibleControlListener, KeyListener, TraverseListener, FocusListener, AccessibleListener, MouseTrackListener
{
…
}
在EventHandler类中的各个方法实现基本上都是直接调用getEventDispatcher().dispatchMouseMoved(event)分发事件。这个过程实际上就是,LightweightSystem接收SWT事件(通过EventHandler类事件的诸多接口方法),然后EventHandler再直接将事件转发到事件分发器中;然后事件分发器将swt事件转换成draw2d事件并分发到恰当的图形元素中。
通过对LightweightSystem类初始化过程的跟踪分析,基本上可以了解到组成LightweightSystem的各个部分是如何被创建的,更重要的是,通过跟踪分析,可以很清晰的了解到draw2d中事件的接收、分发过程。
在EventHandler类中的各个方法实现基本上都是直接调用getEventDispatcher().dispatchMouseMoved(event)分发事件。这个过程实际上就是,LightweightSystem接收SWT事件(通过EventHandler类事件的诸多接口方法),然后EventHandler再直接将事件转发到事件分发器中;然后事件分发器将swt事件转换成draw2d事件并分发到恰当的图形元素中。
通过对LightweightSystem类初始化过程的跟踪分析,基本上可以了解到组成LightweightSystem的各个部分是如何被创建的,更重要的是,通过跟踪分析,可以很清晰的了解到draw2d中事件的接收、分发过程。