图一:Editor类层次图
从图上被选中的那一层起就是GEF中的Editor类层级关系。GraphicalEditor是根类。我们的Editor可以直接实现这个类;如果想带有调色板(palette),则至少需要继承GraphicalEditorWithPalette;如果还想调色板(palette)可收缩,则可继承GraphicalEditorWithFlyoutPalette。在下面的过程中,我们就假设我们实现的都是带调色板(palette)的Editor。
图二
在步骤一中,我们已经有了一个可运行的Editor了。但是如果此时打开这个Editor,会得到一个异常。可以看到异常是发生在getCommandStack方法里的。如果查看GEF的源码就可以发现,这个空指针异常是由getEditDomain()返回的是一个空而造成的。
在每个GEF的Editor里,都需要有一个EditDomain的存在。EditDomain是一个很重要的对象,它维护着GEF中的命令栈(所谓的命令栈就是一个用来存放命令的堆栈。GEF中的所有操作都是通过命令来完成的,这个命令栈就存储所有的这些操作命令。这也能方便的实现redo、undo操作)、负责事件通知等等。所以我们要给我们的Editor设置一个这样的EditDomain。
一般说来我们直接在构造方法里使用一个DefaultEditDomain对象即可,例如:setEditDomain(new DefaultEditDomain(this));当然有可能你也需要实现自己的EditDomain,这超出了本文的讲解范围。
现在我们就有了一个可打开的Editor了,只是现在还没有内容而已。
我们已经有了一个可运行的Editor了,暂时我们先不管这个Editor,我们来看另外一部分:GEF的MVC结构。
GEF全称“图形编辑框架”。简单来说就是一个用来编辑图形的框架,也是一个典型的MVC结构框架。因此要实现一个GEF的应用,主要就是要实现MVC框架中的:模型、视图和控制。
实现模型很简单,通常这部分是根据自己的应用需求来实现的。当然了你也可以用Eclipse里的EMF框架生成的一个应用模型。这里我们定义一个简单的应用模型:HelloWorld,它有一个text属性,有两个方法用来设置和取得这个text属性的值。如下:
图3
GEF中的控件器都需要实现某个EditPart类。一般来说普通的模型结点需要实现AbstractGraphicalEditPart;连接线需要实现AbstractConnectionEditPart等等。我们可以看一下类图:
图四
看我们的HelloWorld(红色圈住的)就继承了AbstractGraphicalEditPart类。需要实现至少两个方法:createFigure()(对应的显示部分)和createEditPolicies()(对应的操作部分)。这里暂时先不讲。
GEF中的视图部分是由控制器(Editpart)负责创建的。看上面控制器部分,最后我们说到的需要实现的两个方法中,其中:createFigure()方法就是用来创建视图的。GEF的视图部分是由Draw2D负责完成的。可以到网上去找一些Draw2D的简单教程看看。例如我们的实现:
@Override protected IFigure createFigure() { Label label = new Label(); label.setBorder(new LineBorder(ColorConstants.red)); HelloWorldModel helloworld = (HelloWorldModel) getModel(); label.setText(helloworld.getText()); label.setOpaque(true); label.setBackgroundColor(ColorConstants.yellow); return label; }
应该来说MVC结构中,模型不应该知道任何有关图形的信息,也不应该包含图形的任何信息,模型和视图之间是通过控制器来打交道的。不过在GEF中,有时为了记住视图的一些状态,我们却不得不在模型中保存一些图形相关的信息,例如:图形大小、图形位置等等。这样当我们下次打开图形时,还可以保持它最后关闭时的状态。这点可能不是太好。在GMF中,这种状态终于有所改变了。GMF中每种模型都有一个view去专门处理图形的信息,而且保存的时候也可以把模型信息与图形信息进行分开保存。
说上面的话,其实我只是想说下面的内容:在HelloWorld中添加一个属性用于保存图形的大小。所以现在我们的HelloWorld模型就变成了:
import org.eclipse.draw2d.geometry.Rectangle; public class HelloWorldModel{ private String text = "<unname>"; private Rectangle constraints = new Rectangle(0,0,100,20); public String getText() { return text; } public void setText(String text) { this.text = text; } public Rectangle getConstraints() { return constraints; } public void setConstraints(Rectangle constraints) { this.constraints = constraints; } }
最后就是将三者联结起来,这里用到了EditPartFactory类,例如:
public class DiagramEditPartFactory implements EditPartFactory { public EditPart createEditPart(EditPart context, Object model) { if(model instanceof HelloWorldModel){ HelloWorldEditPart editPart = new HelloWorldEditPart(); editPart.setModel(model); return editPart; } return null; } }
这样我们就完成了一个模型对象的MVC结构。 假如可以运行的话,我们看到此时运行的效果应该是:
呵呵,现在什么都还没有。
如果我们还要再创建一个模型,就基本上是做重复运动了。这个在GEF是比较烦的一件事。所以在后面的才会出现GMF。只要给出一个能生成模型的东西,比如说XML schema、java annotaion、UML图等,它就自动帮你生成了一个Editor。那几乎是不要几分钟的事情。不过现在我们还得继续我们的事件。。。。