GEF七天之第四天
没办法,这几天一直在生病.也不是太严重.就是在家呆着,老爸把空调弄成20度(老妈解释,冰箱冷藏功能坏了,暂时用空调顶一下)结果呢太凉,到外面又太热.一凉一热俺就病了.看来咱还是不能享受啊!不多说了,咱继续,还有一点,最近在看Junit(暂时是理论).所以呢,gef系列也可能上的慢一些!!
咱们接着上回来说.EditPart我们已经介绍过了.是否还记得createEditPolicies()这个方法.我们给他的解释是"安装相应的策略".比如ShapeEditPart这个类中的:
protected void createEditPolicies() {
//安装删除策略
installEditPolicy(EditPolicy.COMPONENT_ROLE, new ShapeComponentEditPolicy());
//安装建立,更改连接策略
installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new ShapeConnectionEditPolicy());
}
之前,我们已经说过,在EditPart,我们通过不同角色(即就是客户端的操作的抽象),来寻找不同的策略,去执行不同的命令.这样,一方面,可以把代码从EditPart中解放处 理,分别由不同的EditPolicies进行处理,另一方面,用户可以着力于自己的关注点.(我们可以举个例子:用户用鼠标移动活动,其实这过程包括用户向控制器发出移动活动的请求(Request),控制器就调用相应的命令(Command)来修改模型中活动的位置属性,而命令的调用时通过寻找编辑策略来完成的。而模型的位置属性发生变化,又会通知控制器,控制器就会刷新视图,改变活动的位置)
那我们先看上面那个createEditPolicies()中的两个策略。ShapeComponentEditPolicy提供命令将一个图形从图删除。
其角色是 EditPolicy.COMPONENT_ROLE。注意安装策略时要指定相应的角色(Role),角色只是一个标识,在同一个EditPart中不能存在两个相同角色的编辑策略,读者可以在GEF的联机文档(http://help.eclipse.org/help32/index.jsp?topic=/org.eclipse.gef.doc.isv/guide/guide.html )中找到详细的编辑策略、请求和角色说明。其代码是
/**
* 删除策略
* 建立删除命令
* @author hya
* */
protected Command createDeleteCommand(GroupRequest deleteRequest) {
Object parent = getHost().getParent().getModel();
Object child = getHost().getModel();
if (parent instanceof DiagramModel && child instanceof ShapeModel) {
return new DeleteShapeCommand((DiagramModel) parent, (ShapeModel) child);
}
return super.createDeleteCommand(deleteRequest);
}
}
我们可以看到,这个策略中调用了删除图形的命令。我们的命令将在另一个类中定义,他们其实都是对模型的操作。所有的命令都要继承自Command。如上面的DeleteShapeCommand类:
public class DeleteShapeCommand extends Command {
private final ShapeModel child;
/** 容器 */
private final DiagramModel parent;
/**连接点列表(源点) */
private List sourceConnections;
/** 连接点列表(终点) */
private List targetConnections;
/**记录子图是否从容器中删除 */
private boolean isRemoved;
public DeleteShapeCommand(DiagramModel parent, ShapeModel child) {
if (parent == null || child == null) {
throw new IllegalArgumentException();
}
setLabel("shape deletion");
this.parent = parent;
this.child = child;
}
private void addConnections(List connections) {
for (Iterator iter = connections.iterator(); iter.hasNext();) {
Connection conn = (Connection) iter.next();
conn.reconnect();
}
}
public boolean canUndo() {
return isRemoved;
}
public void execute() {
sourceConnections = child.getSourceConnections();
targetConnections = child.getTargetConnections();
redo();
}
public void redo() {
isRemoved = parent.removeChild(child);
if (isRemoved) {
removeConnections(sourceConnections);
removeConnections(targetConnections);
}
}
private void removeConnections(List connections) {
for (Iterator iter = connections.iterator(); iter.hasNext();) {
Connection conn = (Connection) iter.next();
conn.disconnect();
}
}
public void undo() {
if (parent.addChild(child)) {
addConnections(sourceConnections);
addConnections(targetConnections);
}
}
}
command是gef的内部实现,在命令中我们还要定义UNDO/redo的功能。gef将他们发入命令堆栈,以实现多次取消/重做的功能。我们可以看到,命令就是针对模型的一些相关操作。
前面涉及到的第二个策略是针对连接线的增加和更改。
@Override
protected Command getConnectionCompleteCommand(
CreateConnectionRequest request) {
ConnectionCreateCommand cmd = (ConnectionCreateCommand) request
.getStartCommand();
cmd.setTarget((ShapeModel) getHost().getModel());
return cmd;
}
@Override
protected Command getConnectionCreateCommand(CreateConnectionRequest request) {
ShapeModel source = (ShapeModel) getHost().getModel();
ConnectionCreateCommand cmd = new ConnectionCreateCommand(source);
request.setStartCommand(cmd);
return cmd;
}
@Override
protected Command getReconnectSourceCommand(ReconnectRequest request) {
Connection conn = (Connection) request.getConnectionEditPart().getModel();
ShapeModel newSource = (ShapeModel) getHost().getModel();
ConnectionReconnectCommand cmd = new ConnectionReconnectCommand(conn);
cmd.setNewSource(newSource);
return cmd;
}
@Override
protected Command getReconnectTargetCommand(ReconnectRequest request) {
Connection conn = (Connection) request.getConnectionEditPart().getModel();
ShapeModel newTarget = (ShapeModel) getHost().getModel();
ConnectionReconnectCommand cmd = new ConnectionReconnectCommand(conn);
cmd.setNewTarget(newTarget);
return cmd;
}
}
我们通过不同的request来找到不同的命令来执行相关的操作。比如我们要建立一个连接,gef将调用getConnectionCompleteCommand和getConnectionCreateCommand方法,即该策略被要求创建一个连接命令。如果这个方法返回null,表示这个连接不能从所给的模型元素开始。如果允许连接的话,将创建新的命令,并作为起始命令存储在请求中。当用户点击另一个可视图形时,会要求策略提供一个连接完成命令。这是一个根据起始命令创建的新命令,而起始命令中包含了连接结束点的信息。那么创建连接命令的类我们就可以预见它是一系列针对Connection模型的操作:
private Connection connection;
private final ShapeModel source;
private ShapeModel target;
public ConnectionCreateCommand(ShapeModel source) {
if (source == null) {
throw new IllegalArgumentException();
}
this.source = source;
}
public void execute() {
// 建立新连接
connection = new Connection(source, target);
}
public void redo() {
connection.reconnect();
}
//设置目的连接点
public void setTarget(ShapeModel target) {
if (target == null) {
throw new IllegalArgumentException();
}
this.target = target;
}
public void undo() {
connection.disconnect();
}
}
那么过程我们就可以基本连接起来了:接着上面的说,我们增加连接线是执行了相应的命令。对相应的模型进行操作。当模型改变时,我们模型就调用firePropertyChange(String property, Object oldValue,Object newValue) 方法来同时EditPart增加了连接,然后刷新视图,反映改变。 (红色部分,得过程及代码实现见二,三天的帖子)
其他EditPart中安装的策略,和执行的命令原理是一样的,在这里我们不再赘述。