[Eclipse]GEF入门系列(一、Draw2D)

关于Java2D相信大家都不会陌生,它是基于AWT/Swing的二维图形处理包, JDK附带的示 例程序向我们展示了Java2D十分强大的图形处理能力。在Draw2D出现以前,SWT应用程序在这 方面一直处于下风,而Draw2D这个SWT世界里的Java2D改变了这种形势。

可能很多人还不十分了解GEF和Draw2D的关系:一些应用程序是只使用Draw2D,看起来却 和GEF应用程序具有相似的外观。原因是什么,下面先简单解释一下:

GEF是具有标准MVC(Model-View-Control)结构的图形编辑框架,其中Model由我们自己 根据业务来设计,它要能够提供某种模型改变通知的机制,用来把Model的变化告诉Control 层;Control层由一些EditPart实现,EditPart是整个GEF的核心部件,关于EditPart的机制 和功能将在以后的帖子里介绍;而View层(大多数情况下)就是我们这里要说的Draw2D了, 其作用是把Model以图形化的方式表现给使用者。

虽然GEF可以使用任何图形包作为View层,但实际上GEF对Draw2D的依赖是很强的。举例来 说:虽然EditPart(org.eclipse.gef.EditPart)接口并不要求引入任何Draw2D的类,但我 们最常使用的AbstractGraphicalEditPart类的createFigure()方法就需要返回IFigure类型 。由于这个原因,在GEF的SDK中索性包含了Draw2D包就不奇怪了,同样道理,只有先了解 Draw2D才可能掌握GEF。

这样,对于一开始提出的问题可以总结如下:Draw2D是基于SWT的图形处理包,它适合用 作GEF的View层。如果一个应用仅需要显示图形,只用Draw2D就够了;若该应用的模型要求以 图形化的方式被编辑,那么最好使用GEF框架。

现在让我们来看看Draw2D里都有些什么,请看下图。

图1 Draw2D的结构

Draw2D通过被称为LightweightSystem(以下简称LWS)的部件与SWT中的某一个Canvas实 例相连,这个Canvas在Draw2D应用程序里一般是应用程序的Shell,在GEF应用程序里更多是 某个EdiTor的Control(createPartControl()方法中的参数),在界面上我们虽然看不到LWS 的存在,但其他所有能看到的图形都是放在它里面的,这些图形按父子包含关系形成一个树 状的层次结构。

LWS是Draw2D的核心部件,它包含三个主要组成部分:RootFigure是LWS中所有图形的根, 也就是说其他图形都是直接或间接放在RootFigure里的;EventDispatcher把Canvas上的各种 事件分派给RootFigure,这些事件最终会被分派给适当的图形,请注意这个RootFigure和你 应用程序中最顶层的IFigure不是同一个对象,前者是看不见的被LWS内部使用的,而后者通 常会是一个可见的画布,它是直接放在前者中的;UpdateManager用来重绘图形,当Canvas被 要求重绘时,LWS会调用它的performUpdate()方法。

LWS是连接SWT和Draw2D的桥梁,利用它,我们不仅可以轻松创建任意形状的图形(不仅仅 限于矩形),同时能够节省系统资源(因为是轻量级组件)。一个典型的纯Draw2D应用程序 代码具有类似下面的结构:

//创建SWT的Canvas(Shell是Canvas的子类)Shell shell = new Shell();shell.open();shell.setText("A Draw2d application");//创建LightweightSystem,放在shell上LightweightSystem lws = new LightweightSystem(shell);//创建应用程序中的最顶层图形IFigure panel = new Figure();panel.setLayoutManager(new FlowLayout());//把这个图形放置于LightweightSystem的RootFigure里lws.setContents(panel); //创建应用程序中的其他图形,并放置于应用程序的顶层图形中panel.add();while (!shell.isDisposed ()) {if (!display.readAndDispatch ())  display.sleep ();}

接下来说说图形,Draw2D中的图形全部都实现IFigure(org.eclipse.draw2d.IFigure) 接口,这些图形不仅仅是你看到的屏幕上的一块形状而已,除了控制图形的尺寸位置以外, 你还可以监听图形上的事件(鼠标事件、图形结构改变等等,来自LWS的EventDispatcher) 、设置鼠标指针形状、让图形变透明、聚焦等等,每个图形甚至还拥有自己的Tooltip,十分 的灵活。

Draw2D提供了很多缺省图形,最常见的有三类:1、形状(Shape),如矩形、三角形、椭 圆形等等;2、控件(Widget),如标签、按钮、滚动条等等;3、层(Layer),它们用来为 放置于其中的图形提供缩放、滚动等功能,在3.0版本的GEF中,还新增了GridLayer和 GuideLayer用来实现”吸附到网格”功能。在以IFigure为根节点的类树下有相当多的类,不过 我个人感觉组织得有些混乱,幸好大部分情况下我们只用到其中常用的那一部分。

图2 一个Draw2D应用程序

每个图形都可以拥有一个边框(Border),Draw2D所提供的边框类型有GroupBoxBorder、 TitleBarBorder、ImageBorder、ButtonBorder,以及可以组合两种边框的CompoundBorder等 等,在Draw2D里还专门有一个Insets类用来表示边框在图形中所占的位置,它包含上下左右 四个整型数值。

我们知道,一个图形可以包含很多个子图形,这些被包含的图形在显示的时候必须以某种 方式被排列起来,负责这个任务的就是父图形的LayoutManager。同样的,Draw2D已经为我们 提供了一系列可以直接使用的LayoutManager,如FlowLayout适合用于表格式的排列, XYLayout适合让用户在画布上用鼠标随意改变图形的位置,等等。如果没有适合我们应用的 LayoutManager,可以自己定制。每个LayoutManager都包含某种算法,该算法将考虑与每个 子图形关联的Constraint对象,计算得出子图形最终的位置和大小。

图形化应用程序的一个常见任务就是在两个图形之间做连接,想象一下UML类图中的各种 连接线,或者程序流程图中表示数据流的线条,它们有着不同的外观,有些连接线还要显示 名称,而且最好能不交叉。利用Draw2D中的Router、Anchor和LocaTor,可以实现多种连接样 式,其中Router负责连接线的外观和操作方式,最简单的是设置Router为null(无Router) ,这样会使用直线连接,其他连接方式包括折线、具有控制点的折线等等(见图3),若想控 制连接线不互相交叉也需要在Router中作文章。Anchor控制连接线端点在图形上的位置,即” 锚点”的位置,最易于使用的是ChopBoxAnchor,它先假设图形中心为连接点,然后计算这条 假想连线与图形边缘的交汇点作为实际的锚点,其他Anchor还有EllipseAnchor、 LabelAnchor和XYAnchor等等;最后,LocaTor的作用是定位图形,例如希望在连接线中点处 以一个标签显示此连线的名称/作用,就可以使用MidpointLocaTor来帮助定位这个标签,其 他LocaTor还有ArrowLocaTor用于定位可旋转的修饰(Decoration,例如PolygonDecoration )、BendpointerLocaTor用于定位连接控制点、ConnectionEndpointLocaTor用于定位连接端 点(通过指定uDistance和vDistance属性的值可以设置以端点为原点的坐标)。

图3 三种Router的外观

此外,Draw2D在org.eclipse.draw2d.geometry包里提供了几个很方便的类型,如 Dimension、Rectangle、Insets、Point和PointList等等,这些类型既在Draw2D内部广泛使 用,也可以被开发人员用来简化计算。例如Rectangle表示的是一个矩形区域,它提供 getIntersection()方法能够方便的计算该区域与另一矩形区域的重叠区域、getTransposed ()方法可以得到长宽值交换后的矩形区域、scale()方法进行矩形的拉伸等等。在自己实现 LayoutManager的时候,由于会涉及到比较复杂的几何计算,所以更推荐使用这些类。

以上介绍了Draw2D提供的大部分功能,利用这些我们已经能够画出十分漂亮的图形了。但 对大多数实际应用来说这样还远远不够,我们还要能编辑它,并把对图形的修改反映到模型 里去。为了漂亮的完成这个艰巨任务,GEF绝对是不二之选。从下一次开始,我们将正式进入 GEF的世界。

不敢接受失败的人,往往是那些追求完美的人,

[Eclipse]GEF入门系列(一、Draw2D)

相关文章:

你感兴趣的文章:

标签云: