离开GEF项目组已经一个多月了,但是那段日子给我很深的印象,因为那仿佛是我真正下定决心,学习成为一个好的IT人员之路的开始。而且开始积累编程经验。GEF并不简单,面对这个拦路虎,我很是兴奋,想把那段时间的学习体会以及我们的项目来个大致的介绍。
我们的项目是一个Eclipse平台上的用于本体建模的图形编辑器,大家决定采用GEF框架,因为它非常适合开发带有调色板的以编辑器为主要部件的应用程序。刚开始一个星期,非常迷茫,不知从何下手。这里要非常感谢八进制的blog,他写的关于GEF的系列给了我很大的帮助,至今我还有很多没有完全消化的地方。
一. 入门
我们选取了一个GefPractice的例子作为学习的对象。它的结构很清楚,包划分得很容易理解(现在发现包的划分基本是按照gef api中包的划分来的)。它实现了一个简单得编辑器的功能,可以创建节点、编辑节点和删除节点,还可以创建节点之间的连接。它一共划分了10个包:
1. ui包:这里是用户界面,主要是编辑器editor,编辑器一般是继承GraphicalEditorWithPalette,这样我们就可以得到一个GraphicalViewer和一个PaletteViewer。我认为学习从ui入手比较简单,因为我们一般和editor接触比较多,对于GEF内部工作机制的了解也应从editor开始,这样更直观。Editor中有几个重要的概念:
① 在editor的构造函数中,调用setEditDomain()方法,而使用的一般就是DefaultEditDomain(),它是EditDomain的一个默认实现。它是一个GEF程序的状态集合,包括命令栈,一个或多个EditpartViewer,和当前的活动工具。
② 两个方法:configureGraphicalViewer()和initialGraphicalViewer(). configureCraphicalViewer()方法中替graphicalViewer设置rootEditPart,设置EditPartFactory。InitalGraphicalViewer()方法中,替graphicalViewer设置contentProvider,一般是画布(model包中),设置拖放监听器(这个以后还要重点说明)。
③ GetAdapt()方法,应该是视图之间的切换(这个现在还不太清楚)。
④ 内部类继承ContentOutlinePage实现大纲视图。
2.model包:
模型:GEF的模型只与控制器打交道,而不知道任何与视图有关的东西。为了能让控制器知道模型的变化,应该把控制器作为事件监听者注册在模型中,当模型发生变化时,就触发相应的事件给控制器,后者负责通知各个视图进行更新。
类之间的层次关系
在org.sklse.oed本题编辑器项目中,创建了org.sklse.oed.model包来装载模型,在设计中,我们结合java程序面向对象的特点,将创建的类抽象成三个层次,如下图:
根类:AbstractModel.class
AbstractModel作为整个模块的根类,实现模型层接点和连线公用的监听接口,属性变化监听事件。主要是通过implements IpropertySource来实现的,IpropertySource是eclipse中关于属性视图开发方面的接口。
结点父类:AbstractNodeModel.class
该类抽象出结点公用的一些属性和方法,比如:定义了P_CONSTRAINT、P_TEXT、P_SOURCE_CONNECTION、P_TARGET_CONNECTION等属性,其中P_CONSTRAINT用来布局的,P_TEXT是模型的名字,而P_SOURCE_CONNECTION、P_TARGET_CONNECTION用来存放模型的源连接和目的连接的链表。
然后根据这些属性定义一系列相关的操作,比如:
public void addSourceConnection(Object connx) {
sourceConnections.add(connx);
firePropertyChange(P_SOURCE_CONNECTION, null, null);
}
上面就是一个很典型的方法,目的是给模型对象添加一个源连接,参数connx表示源连接,sourceConnections.add(connx);是源链表的添加方法,firePropertyChange(P_SOURCE_CONNECTION, null, null)方法来激活属性变化事件。正如mvc模式的思想一样,在模型被修改的同时,通过消息事件通知控制层(firePropertyChange),后者根据模型变化来修改视图。
连线父类:AbstractConnectionModel.class
该类抽象出连线公用的一些属性和方法,连线最重要的属性就是P_BEND_POINT ,我把它理解成拐点,即可以通过拖动这个拐点来折变连线。一条连线就必须有源接点和目的接点,即source, target,在model层增加一个连线,也就等于在source和target的源接点连表和目的接点链表上增加这条记录。有这类方法:
public void attachSource() {
if (!source.getModelSourceConnections().contains(this))
source.addSourceConnection(this);
}
对于P_BEND_POINT属性的变化也必须有一套方法:
public void addBendpoint(int index, Point point) {
bendpoints.add(index, point);
firePropertyChange(P_BEND_POINT, null, null);
}
画板模型:ContentModel.class
编辑区的画板模型,相当于母版,其他图形都是添加到它上面的。
//定义一个属性
public static final String P_CHILDREN = "_children";
//定义一个存放孩子接点的链表
private List children = new ArrayList();
public void addChild(Object child) {//添加孩子结点
children.add(child);
firePropertyChange(P_CHILDREN, null, null);//激活属性变化事件
}