IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope:Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Open source  >

GEF 进阶,第 5 部分: Viewer

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

样例代码


级别: 中级

马 若劼 (maruojie@cn.ibm.com), 软件工程师, IBM 中国软件开发中心

2007 年 10 月 25 日

Viewer是GEF中顶层的界面组件,可以认为Viewer就是一块画板,里面放什么东西完全可以由你控制。在GEF中,这样的画板不止一块,其外观也不太相同,我们也可以添加自己的Viewer。Viewer在内部应用了MVC的设计模式,要自定义一个Viewer,必须完成MVC的所有元素,本文演示了这个基本的过程。

Viewer是GEF中顶层的界面组件,可以认为Viewer就是一块画板,里面放什么东西完全可以由你控制。在GEF中,这样的画板不止一块,其外观也不太相同,我们也可以添加自己的Viewer。Viewer在内部应用了MVC的设计模式,要自定义一个Viewer,必须完成MVC的所有元素,本文演示了这个基本的过程。

Viewer

GEF中的一些常见的组件其实都是Viewer,如下图所示:


图 1. GEF中一些缺省的Viewer
GEF中一些缺省的Viewer

图1列出了GEF中一些常见的Viewer,可以看出来它们的外观上有不小的差异,但是它们在本质上都是一样的。它们都实现了EditPartViewer接口,并且都是MVC模式的。我们不要被它们的外观所蒙蔽。

上面提到的EditPartViewer接口是GEF中的Viewer必须实现的一个接口,这是一个较为庞大的接口。仔细的浏览其方法,可以粗略的了解到Viewer的一些能力,比如拖放支持,上下文菜单,键盘事件等等。从这个接口派生出了很多类,但是基本上可以分为两类:有画板支持和无画板支持的。比如从AbstractEditPartViewer派生出来两个子类:GraphicalViewerImpl和TreeViewer。GraphicalViewerImpl也就是我们通常进行可视化编辑的那块区域。而TreeViewer则是图1中Outline视图显示的内容,在Outline视图中,我们没有办法进行所见即所得的编辑,也就是我说的“没有画板支持”。

GEF自带的一些Viewer已经可以满足我们大部分的需要,如果你有一些特殊的需求,需要实现一个特别的Viewer,也可以很容易的做到。本文的其余部分就来添加一个简单的Viewer,逐步的解释添加Viewer时需要了解的概念和注意的问题。





回页首


自定义Viewer

我们打算在shapes示例代码的基础上,在编辑区域添加一个Viewer,这个viewer的基本功能就是按顺序显示所有的图形,但是不显示连线,同时在选择其中的图形的时候,主编辑区域的图形也会被选择。

模型层

由于Viewer是MVC架构的组件,因此要添加一个自定义的Viewer,比如兼顾MVC的所有元素,首先是模型层。幸运的是,我们不需要自定义什么模型,当然你可以这样做,不过在本文的例子中,我们沿用shapes示例代码中的模型。

表示层

从现有的类中继承是快速实现自定义Viewer的方法,我们可以从ScrollingGraphicalViewer继承出我们自己的Viewer,如下所示:


清单1. 继承ScrollingGraphicalViewer
                public class ShapeViewer extends ScrollingGraphicalViewer {
   protected void hookControl() {
      super.hookControl();
      FigureCanvas canvas = getFigureCanvas();
      canvas.getViewport().setContentsTracksWidth(true);
      canvas.getViewport().setContentsTracksHeight(false);
      canvas.setHorizontalScrollBarVisibility(FigureCanvas.AUTOMATIC);
      canvas.setVerticalScrollBarVisibility(FigureCanvas.NEVER);
   }
}

我定义了一个ShapeViewer作为我们的表示层,这个类很简单,只覆盖了父类的hookControl()方法。hookControl()本质上只是做一些初始化工作,比如配置一下滚动条。我们可以覆盖或者添加更多的方法,但是我并不打算把它弄的很复杂,为了方便我们理解这个过程,代码越少越好。

控制层

控制层是工作相对多的一块,首先的一个问题是:我们可以不可以重用shapes示例中已有的那些EditPart呢?大部分代码是可以重用的,因为我们这个Viewer也是有画板支持的,所以本质上也都应该继承自AbstractGraphicalEditPart。但是有些代码,比如连线相关的代码,我们就不需要,因为我们不支持显示连线。还有一些EditPolicy相关的代码,则要看你的具体需要了,如果需要相关的角色,则可以重用。为了简单起见,我们使用了FlowLayout来安排图形,因此我们为Diagram添加了FlowLayoutEditPolicy。因为我们希望代码越简单越好,所以我们不支持创建Command,全部设为返回null。

由于这部分的代码基本上是shapes已有代码的一个子集,所以我们不一一列出,大家可以在随本文提供的源代码中看到细节。这里只提一下我为Diagram和Shape分别创建了EditPart,名为SimpleDiagramEditPart和SimpleShapeEditPart.

既然我们给自己的Viewer定义了一些EditPart,就必然需要一个EditPart工厂。如下:


清单2. 为我们的Viewer创建EditPart工厂
                public class SimpleEditPartFactory implements EditPartFactory {
   public EditPart createEditPart(EditPart context, Object model) {
      EditPart part = null;
      if(model instanceof ShapesDiagram)
         part = new SimpleDiagramEditPart();
      else if(model instanceof Shape)
         part = new SimpleShapeEditPart();
         
      if(part != null)
         part.setModel(model);
         
      return part;
   }
}

到这里为止,我们的控制层就算完成了。出于简单的考虑,很多EditPolicy和Command还没有加上,但是它已经有个样子了。其它的功能作为练习留给读者完成。

组装与初始化

可是我们的工作并没有做完,Viewer虽然有了,可是显示在哪里呢?我们还需要把它添加到编辑器中,而且这一部分也并非平淡如水,还是有不少工作可以做的。我们先给出完成后的ShapeEditor的createPartControl()代码:


清单3. 组装各个部分
                public void createPartControl(Composite parent) {
   Composite topLevel = new Composite(parent, SWT.NONE);
   GridLayout layout = new GridLayout();
   layout.marginHeight = 0;
   layout.marginWidth = 0;
   layout.verticalSpacing = 0;
   topLevel.setLayout(layout);
   
   Composite top = new Composite(topLevel, SWT.NONE);
   top.setLayout(new FillLayout());
   top.setLayoutData(new GridData(GridData.FILL_BOTH));
   
   Composite bottom = new Composite(topLevel, SWT.BORDER);
   bottom.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
   bottom.setLayout(new FillLayout());
   
   super.createPartControl(top);
   
   ShapeViewer viewer = new ShapeViewer();
   viewer.createControl(bottom);
   getEditDomain().addViewer(viewer);
   getSelectionSynchronizer().addViewer(viewer);
   viewer.setEditPartFactory(new SimpleEditPartFactory());
   viewer.setRootEditPart(new ScalableRootEditPart());
   viewer.setContents(getModel());
}

为了给新的Viewer腾一个地方,我把编辑器划分成上下两个部分,上面的部分仍然是传统的可视化编辑区和调色板, 下面就用来放置新的Viewer。值得注意的是最后五行,首先,最后三行是把我们的模型层,表示层和控制层连接了起来, 这是必须的,就好像机器要运转,零件一个也不能少的道理一样。其次,倒数第四第五行是一些附加工作,没有也没有 关系,不过我们还是应该明白它们是什么意思。getEditDomain().addViewer(viewer)把 我们的Viewer添加到了EditDomain中,这样EditDomain会记住我们的Viewer并且向我们派发一些事件,比如鼠标事件。 而getSelectionSynchronizer().addViewer(viewer)的作用是把我们的Viewer里面的选择 事件和其它Viewer同步起来,这样做的效果是用户在其它Viewer里选择了一个图形之后,我们的Viewer里面也会反映出来, 反之亦然。

还有很多其它的事可以做,比如装载键盘事件处理器,添加拖放支持,等等。所以我把这一部分的工作总结为“组装和初始化”。具体的步骤不一一列举了,留待读者完成。

完成后的Viewer如下图所示:


图2. 自定义Viewer效果图
自定义Viewer效果图

图2中看到,我实现了所有计划的功能,把所有的图形按照创建的顺序排成了一排,并且上下两个Viewer的选择也是同步的。





回页首


Viewer之间的交互

有了多个Viewer之后,一个主要的问题是它们应该如何交互?让Viewer之间互相知道对方并不是一个好的设计,观察GEF中各个Viewer之间的关系,基本上都使用了一些手段来降低它们之间的耦合。比如调色板和编辑区之间通过Request隔离开两个Viewer,标尺和编辑区之间通过Ruler/Guide模型来交互。如果我们需要在我们的Viewer和其它Viewer之间交互,可以参考这样的设计。





回页首


结束语

GEF中的几乎一切东西都可以定制,本文介绍的是Viewer的定制,Viewer是GEF中的顶层结构,其内部结构为MVC模式,可以认为一个Viewer就是一个画板,而我们也可以定制自己的画板。本文提供的代码是一个很基本的例子,还有很多功能留待读者去完成。





回页首


声明

本文仅代表作者的个人观点,不代表IBM的立场。






回页首


下载

描述名字大小下载方法
Viewer示例代码org.eclipse.gef.examples.shapes_viewer.zip49KBHTTP
关于下载方法的信息


参考资料



关于作者

马若劼,IBM 公司软件工程师,主要从事 Workplace Forms 的设计与开发。他在 Java,Eclipse 以及 Eclipse 插件技术方面拥有多年经验,同时也是开源项目 LumaQQ 的创立者。




对本文的评价

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)

建议?




回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款