之前我还是要说,学习GEF,如果你没有学过Eclipse插件,那么最好先花时间熟悉一下Eclipse的插件体系结构,在这里我只介绍GEF的相关知识,swt以及eclipse plug-in的东西还需自己学习.(为此偶花了100大元买了IBM的eclipse 开发指南 )
从前面的介绍中我们知道,GEF采用MVC模式,所以我们就从模型开始.
模型实现的功能主要有以下几个方面:(1)存储了所有用户可以编辑或浏览的数据,包括可视化相关的数据比如大小,坐标等
(2)提供可持久化模型的方法,当编辑器关闭时你的模型被持久化.打开后还应恢复.
(3)提供方法允许 别人监听他的变化,前面的介绍中我们知道,控制要知道模型的变化.并作出相应的相应.
根据以上三个方面我们在看DEMO的要求. 三角,矩形,圆形均他们具有相同的特征,所以我们不妨将其抽象为ShapeModel类,由于 模型的属性变化了,必须通知控制器,由它来刷新模型对应的视图,所以控制器必须注册为模型的侦听器。由于每个模型都有相应的控制器侦听器侦听它属性的变化,我们把这方面的功能都放在父类中,定义一个ElementModel父类(觉得这个挺好,网上的各种示例都这么干的)
所以我们先来看看模型ElementModel父类的代码:
package
hya.gef.demo.shapes.models;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
/** */ /**
* 基本的模型元素
* <p>
* 所有模型必须继承此类
* </p>
* <p>
* 实现Serializable接口,持久化
* </p>
*
* @author hya
*/
public abstract class ElementModel implements Serializable {
// 表明类的不同版本间的兼容性,序列化
private static final long serialVersionUID = 1;
/** *//** 构造一个 PropertyChangeSupport 对象,绑定this类,非序列化 */
private transient PropertyChangeSupport pcsDelegate = new PropertyChangeSupport(
this);
/** *//** 注册监听器 */
public synchronized void addPropertyChangeListener(PropertyChangeListener l) {
if (l == null) {
throw new IllegalArgumentException();
}
pcsDelegate.addPropertyChangeListener(l);
}
/** *//** 发生改变通知监听器 */
protected void firePropertyChange(String property, Object oldValue,
Object newValue) {
if (pcsDelegate.hasListeners(property)) {
pcsDelegate.firePropertyChange(property, oldValue, newValue);
}
}
//序列化
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
pcsDelegate = new PropertyChangeSupport(this);
}
//撤销监听
public synchronized void removePropertyChangeListener(
PropertyChangeListener l) {
if (l != null) {
pcsDelegate.removePropertyChangeListener(l);
}
}
}
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
/** */ /**
* 基本的模型元素
* <p>
* 所有模型必须继承此类
* </p>
* <p>
* 实现Serializable接口,持久化
* </p>
*
* @author hya
*/
public abstract class ElementModel implements Serializable {
// 表明类的不同版本间的兼容性,序列化
private static final long serialVersionUID = 1;
/** *//** 构造一个 PropertyChangeSupport 对象,绑定this类,非序列化 */
private transient PropertyChangeSupport pcsDelegate = new PropertyChangeSupport(
this);
/** *//** 注册监听器 */
public synchronized void addPropertyChangeListener(PropertyChangeListener l) {
if (l == null) {
throw new IllegalArgumentException();
}
pcsDelegate.addPropertyChangeListener(l);
}
/** *//** 发生改变通知监听器 */
protected void firePropertyChange(String property, Object oldValue,
Object newValue) {
if (pcsDelegate.hasListeners(property)) {
pcsDelegate.firePropertyChange(property, oldValue, newValue);
}
}
//序列化
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
in.defaultReadObject();
pcsDelegate = new PropertyChangeSupport(this);
}
//撤销监听
public synchronized void removePropertyChangeListener(
PropertyChangeListener l) {
if (l != null) {
pcsDelegate.removePropertyChangeListener(l);
}
}
}
从中可以看到为了持久化,我们让其实现了
java.io.Serializable
接口以及
readObject方法.同时我为了提供监听机制,我们引入了PropertyChangeSupport对象,同时添加addPropertyChangeListener和addPropertyChangeListener两个方法来提供监听器的注册和移除.同时提供firePropertyChange(String property, Object oldValue,Object newValue) 方法来同志监听器模型属性发生了变化.
然后我们在来看我们的抽象图形类,取名为ShapeModel
/** *//**
* 图形的公共基类
* <UL>
* <LI>所有图形继承此类<LI>
* <LI>抽象了图形的公共属性位置,大小,以及连线<LI>
* </UL>
* @author hya
* */
public abstract class ShapeModel extends ElementModel {
/** *//**坐标改变的ID, 用于通知监听器图形坐标放生改变 */
public static final String LOCATION_PROP = "Model.Location";
private static final long serialVersionUID = 1;
/** *//** 大小改变的ID,通知监听器图形大小放生改变 */
public static final String SIZE_PROP = "Model.Size";
/** *//**以下连个为连接ID,用于通知监听器图形的连接发生改变 */
public static final String SOURCE_CONNECTIONS_PROP = "Model.SourceConn";
public static final String TARGET_CONNECTIONS_PROP = "Model.TargetConn";
/** *//** 图形坐标 */
private Point location = new Point(0, 0);
/** *//** 图形大小 */
private Dimension size = new Dimension(50, 50);
/** *//** 图形作为连接源点的列表 */
private List sourceConnections = new ArrayList();
/** *//** 图形作为连接终点的列表*/
private List targetConnections = new ArrayList();
/** *//**
* 图形增加连接
*/
void addConnection(Connection conn) {
if (conn == null || conn.getSource() == conn.getTarget()) {
throw new IllegalArgumentException();
}
if (conn.getSource() == this) {
sourceConnections.add(conn);
firePropertyChange(SOURCE_CONNECTIONS_PROP, null, conn);
} else if (conn.getTarget() == this) {
targetConnections.add(conn);
firePropertyChange(TARGET_CONNECTIONS_PROP, null, conn);
}
}
/** *//**
* 根据路径得到图标
* 相对于类Activator
* */
protected static Image createImage(String name) {
InputStream stream = Activator.class.getResourceAsStream(name);
Image image = new Image(null, stream);
try {
stream.close();
} catch (IOException ioe) {
}
return image;
}
/** *//**
*定义元素模型图标
*子类应实现它
*/
public abstract Image getIcon();
/** *//**
* 得到图形坐标
*/
public Point getLocation() {
return location.getCopy();
}
/** *//**
* 得到图形大小
*/
public Dimension getSize() {
return size.getCopy();
}
public List getSourceConnections() {
return new ArrayList(sourceConnections);
}
public List getTargetConnections() {
return new ArrayList(targetConnections);
}
/** *//**
*移出连接
*/
public void removeConnection(Connection conn) {
if (conn == null) {
throw new IllegalArgumentException();
}
if (conn.getSource() == this) {
sourceConnections.remove(conn);
firePropertyChange(SOURCE_CONNECTIONS_PROP, null, conn);
} else if (conn.getTarget() == this) {
targetConnections.remove(conn);
firePropertyChange(TARGET_CONNECTIONS_PROP, null, conn);
}
}
/** *//**
* 改变图形坐标
*/
public void setLocation(Point newLocation) {
if (newLocation == null) {
throw new IllegalArgumentException();
}
location.setLocation(newLocation);
firePropertyChange(LOCATION_PROP, null, location);
}
/** *//**
*改变图形大小
*/
public void setSize(Dimension newSize) {
if (newSize != null) {
size.setSize(newSize);
firePropertyChange(SIZE_PROP, null, size);
}
}
}
由于图形之间要有连线,这个连线通常是有方向的所以我们提供了两个List来分别存储源连接和目标连接。源连接是指那些以当前图形作为源的连接,目标连接是指以当前图形作为目标的连接。同时的还有两个对象属性
location,size 来表示图形的坐标和大小。同时提供了各自的方法来维护这些属性,我们应该注意到每次模型发生改变时都会调用firePropertyChange();来通知监视器模型有改变,他维护了三个参数第一个为属性表示,是我们自己定义的用来告诉监视器那个属性有变化以及变化的值。(看到这里你是不是会想到什么,对就是观察者模式,属性监听机制就是用了观察者模式)。
三个图形的只是对它的扩展
/** */ /**
*三角型
* @author hya
* */
public class TriangleModel extends ShapeModel {
private static final Image RECTANGLE_ICON = createImage("icons/triangle.gif");
private static final long serialVersionUID = 1;
public Image getIcon() {
return RECTANGLE_ICON;
}
public String toString() {
return "Triangle " + hashCode();
}
}
是不是忘了什么,对就是连接线模型
/** */
/**
*连接线模型
* @author hya
*/
public class Connection extends ElementModel {
//表明类的不同版本间的兼容性,序列化
private static final long serialVersionUID = 1;
private boolean isConnected;
/** *//**源点 */
private ShapeModel source;
/** *//**目的节点*/
private ShapeModel target;
/** *//**
* 初始化类时建立一个连接
*/
public Connection(ShapeModel source, ShapeModel target) {
reconnect(source, target);
}
/** *//**
* 删除连接
*/
public void disconnect() {
if (isConnected) {
source.removeConnection(this);
target.removeConnection(this);
isConnected = false;
}
}
/** *//**
* 得到连接的源点
*/
public ShapeModel getSource() {
return source;
}
/** *//**
*得到连接的目的点
*/
public ShapeModel getTarget() {
return target;
}
/** *//**重新建立连接*/
public void reconnect() {
if (!isConnected) {
source.addConnection(this);
target.addConnection(this);
isConnected = true;
}
}
/** *//**建立连接*/
public void reconnect(ShapeModel newSource, ShapeModel newTarget) {
if (newSource == null || newTarget == null || newSource == newTarget) {
throw new IllegalArgumentException();
}
disconnect();
this.source = newSource;
this.target = newTarget;
reconnect();
}
}
*连接线模型
* @author hya
*/
public class Connection extends ElementModel {
//表明类的不同版本间的兼容性,序列化
private static final long serialVersionUID = 1;
private boolean isConnected;
/** *//**源点 */
private ShapeModel source;
/** *//**目的节点*/
private ShapeModel target;
/** *//**
* 初始化类时建立一个连接
*/
public Connection(ShapeModel source, ShapeModel target) {
reconnect(source, target);
}
/** *//**
* 删除连接
*/
public void disconnect() {
if (isConnected) {
source.removeConnection(this);
target.removeConnection(this);
isConnected = false;
}
}
/** *//**
* 得到连接的源点
*/
public ShapeModel getSource() {
return source;
}
/** *//**
*得到连接的目的点
*/
public ShapeModel getTarget() {
return target;
}
/** *//**重新建立连接*/
public void reconnect() {
if (!isConnected) {
source.addConnection(this);
target.addConnection(this);
isConnected = true;
}
}
/** *//**建立连接*/
public void reconnect(ShapeModel newSource, ShapeModel newTarget) {
if (newSource == null || newTarget == null || newSource == newTarget) {
throw new IllegalArgumentException();
}
disconnect();
this.source = newSource;
this.target = newTarget;
reconnect();
}
}
原理和上面一样,在下一天中我们会来讨论GEF的核心控制器。