GEF是一个完善的图形编辑框架,它提供了图形编辑的解决方案,用户可以通过它的机制构造出功能非常强大的图形编辑器,但GEF还是比较复杂的框架,在提供了图形编辑的各个环节松耦合的灵活性之时,也带来了用户必须了解各环节实现的学习时间。
学习GEF框架,读者除了需要掌握模型、视图和控制器外,还应该了解请求和编辑策略、命令以及和Eclipse相关的其它技术等。
命 令
为了提高代码的重用性,以及实现GEF编辑器的功能,GEF采用命令模式提供了对模型的操作方式。
命令的功能
在EditPolicies中新建了命令(Commands),如果返回的命令不为空,GEF框架会执行命令,对模型进行相应的修改。
Command是GEF中的一个抽象类,主要实现如下几个方法。
1. execute:这是命令的执行方法,当请求结束并获得Command后,GEF框架会负责执行此方法。
2. undo:对模型修改后,可以通过undo进行撤销。
3. redo:当用户撤销后,能通过redo重复上一次的操作。
命令的实现
GEF通过一个命令堆栈(CommandStack)保存Command对象。用户可以通过命令堆栈撤销或重复对模型所做的操作。下面代码演示模型位置改变的命令,如例程1所示。
例程1 MoveNodeCommand.java
public class MoveNodeCommand extends Command {
//命令对应的模型
private Node node;
//原始位置
private Point oldPos;
//新位置
private Point newPos;
//设置新位置
public void setLocation(Point p) {
this.newPos = p;
}
//设置命令对应的模型
public void setNode(Node node) {
this.node = node;
}
//执行命令
public void execute() {
//保存模型原来的位置
oldPos = this.node.getLocation();
node.setLocation(newPos);
}
public String getLabel() {
return "Move Node";
}
//重复上一操作
public void redo() {
this.node.setLocation(newPos);
}
//撤销设置新位置的操作
public void undo() {
this.node.setLocation(oldPos);
}
}
通过命令堆栈(CommandStack),GEF编辑器能轻松完成用户的撤销和重做操作,GEF框架会维护命令堆栈。
模 型 同 步
为了保证数据和展现的同步,必须要有一种消息通知机制,当模型被修改时通知相应的视图进行更新,下面将介绍如何实现这种消息机制。
模型同步的功能
模型是GEF中的实体,当模型的属性被修改后,控制器会监听到模型属性改变的事件,然后通知视图进行更新,模型和视图之间没有直接的联系,如图1所示。
图1 模型、控制器和视图的关系
要通过事件的通知机制获得模型同步的步骤如下。
1. 模型中包含注册监听器的机制,可以通过PropertyChangeSupport和PropertyChangeListener机制添加模型监听器。
2. 把控制器注册为模型监听器,在此通过控制器实现PropertyChangeListener接口,并把自己添加到模型的监听器即可。
3. 当模型触发属性改变的事件后,控制器监听到事件并相应地更新视图。
另外,并不是模型的所有属性的修改都必须通知控制器,一般情况下,当模型的属性更改后要引起视图的更新时,必须触发事件通知控制器,因为模型和视图之间没有任何的关联。
模型同步的实现
典型的模型对象会包含PropertyChangeSupport类型的成员变量,用来触发事件,通知监听器即控制器。当模型的属性被修改后,控制器将更新视图的显示。例如,某个节点的名字修改后,将要触发相应的事件,通知控制器,如下代码如示。
public void setName(String name) {
if (this.name.equals(name)) {
return;
}
this.name = name;
//当模型名字改变后,通知监听器更新名字的显示
firePropertyChange(PROP_NAME, null, name);
}
当模型的名字改变后,将通过firePropertyChange方法触发模型名字被修改的事件,其中第一个参数为事件类型标识(唯一的标识),表示模型的什么属性被改变,第二个参数为改变前的值,第三个参数为改变后的新值。
模型触发事件后,控制器将监听到模型属性修改的事件,通过事件的类型,即firePropertyChange方法的第一个参数判断是哪一个属性被修改,然后执行相应的操作,如下代码所示。
//监听模型属性改变
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(Node.PROP_LOCATION))
//更新视图
refreshVisuals();
else if (evt.getPropertyName().equals(Node.PROP_NAME))
refreshVisuals();
else if (evt.getPropertyName().equals(Node.PROP_INPUTS))
//更新连接
refreshTargetConnections();
else if (evt.getPropertyName().equals(Node.PROP_OUTPUTS))
refreshSourceConnections();
}
当控制器监听到模型的改变事件后,将会通过视图的更新,从而实现了模型数据和视图显示的同步。
选 项 板
选项板提供了用户选择模型元素,并提供相应的机制把模型和视图加入图形编辑器的能力。
选项板(PaletteRoot)的功能
GEF是一个图形编辑框架,它的主编辑区域是一个图形的视图(类似JFace的Viewer),另外,用户还可以选择选项板上相应的模型,把选择的模型对应的视图加入到图形编辑区域进行编辑。
在GEF中,图形编辑器实现类的getPaletteRoot 方法将返回一个PaletteRoot对象,用户可以在PaletteRoot中添加模型选项入口项(ToolEntry)。例如,在选择板中加入一个连线模型的入口,可以加入“new ConnectionCreationToolEntry("Connection", "Create a connection", null, null, null);”,ToolEntry的参数描述了选项板中选项的显示标签、显示图标、提示信息、模型的模板类及模型的创建工厂类等信息。
选项板的实现
在选项板中创建模型的入口,能添加模型的选择项(ToolEntry)和模型分组项(PaletteDrawer),代码如例程2所示。
例程2 PaletteFactory.java
public class PaletteFactory {
public static PaletteRoot createPalette() {
//新建选项板
PaletteRoot paletteRoot = new PaletteRoot();
//添加选项板中的模型选项
paletteRoot.addAll(createCategories(paletteRoot));
return paletteRoot;
}
private static List createCategories(PaletteRoot root) {
List categories = new ArrayList();
//添加分组选项
categories.add(createControlGroup(root));
categories.add(createComponentsDrawer());
return categories;
}
private static PaletteContainer createControlGroup(PaletteRoot root) {
PaletteGroup controlGroup = new PaletteGroup("Control Group");
List entries = new ArrayList();
//添加取消选择模型项
ToolEntry tool = new SelectionToolEntry();
entries.add(tool);
//设置SelectionToolEntry为默认选项
root.setDefaultEntry(tool);
//添加连线模型选项入口
tool = new ConnectionCreationToolEntry("Connection", "Create a connection",
null, null, null);
entries.add(tool);
controlGroup.addAll(entries);
return controlGroup;
}
private static PaletteContainer createComponentsDrawer() {
//添加组件分组页
PaletteDrawer drawer = new PaletteDrawer("Components");
List entries = new ArrayList();
//添加Node模型选项入口
ToolEntry tool = new CombinedTemplateCreationEntry("Node", "Create a new
Node", Node.class, new SimpleFactory(
Node.class), null, null);
entries.add(tool);
drawer.addAll(entries);
return drawer;
}
}
上面代码加入了连线模型、分组项、取消选择项和一个节点项(Node模型)。建立的选项板如图2所示。
图2 选项板
下载(com.free.platform.umleditor.demo.rar)
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/13081368/viewspace-405460/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/13081368/viewspace-405460/