在这说一下,其实GEF七天,并不是把我每天知识的总结,而是学习了GEF七天,我想通过这样一种方式将我这七天的收获和对GEF框架的理解,写出来,也算是对这一周的总结吧
其实这一章我们介绍gef的核心控制器了,在写之前,我们先介绍一下GEF要用到的设计模式.
在学GEF中给我感受最深的是,设计模式无处不在,除了上章中说的观察者模式外.GEF框架还运用了COMMAND(命令)模式,
Policy(策略)模式.这两个模式,策略模式,貌似本团对有人介绍过了,而command模式是我现研究的(也许会单独写个随笔,谈谈心得).
好了,我们言归正传.先说一下,GEF控制器的实现原理.
控制器是由一组EditPart对象共同组成的,每一个模型对象都对应一个EditPart对象。应用程序中需要有一个EditPartFactory对象负责根据给定模型对象创建对应的EditPart对象,这个工厂类将在创建模型时被调用。
控制器是GEF中最复杂的一部分,GEF把控制器完成的工作又分成了几个部分,包括请求和编辑策略及引申出来的命令模式,如下图:
用户的编辑操作被转换为一系列请求(Request),Eclipse中有很多种类的请求,这些种类在GEF里被成为角色(Role)。在GEF里有图形 化和非图形化这两大类角色,前者如“LayoutRole”对应和布局有关的操作,后者如“ConnectionRole”对应和连接有关的操作等。角色 这个概念是通过编辑策略(EditPolicy)来实现的,EditPolicy的主要功能是根据请求创建相应的命令(Command),而后者会直接操 作模型对象。
对每一个EditPart,用户都可以“安装”一些EditPolicy。用户对这个EditPart的特定操作会被交给已安装的对应EditPolicy处理。这样做的直接好处是可以在不同EditPart之间共享一些重复操作。
这样说有点抽象,还是用代码说话,接着上回那个DEMO来所,该是editpart部分了.
对于模型的每个独立部分,我们都必须定义控制器。所谓“独立”,指的是这个实体 都可以作为用户操作的对象。一个比较好的原则就是任何可以被选择,或删除的对象 都应该有它自己的编辑部件(来自于IBM)。
所有的部件均实现了createFigure()(返回模型在视图中的图形表示), createEditPolicies()(安装相应的策略),refreshVisuals() (刷新视图), propertyChange()(接受模型改变并执行不同的操作,刷新视图) ,activate()(自 己加入设为监听器) 和 deactivate()(将自己从监听器的列表中移除)五个方法。
所以由上回的模型,我们就可以建立控制器了,首先我们将他也抽象化:
现建基类SpecificPart
package hya.gef.demo.shapes.parts;

import hya.gef.demo.shapes.models.ElementModel;

import java.beans.PropertyChangeListener;

import org.eclipse.gef.editparts.AbstractGraphicalEditPart;



/** */ /**
* editpart基类
* 主要为注册自己为模型改变的监听者
* @author hya
* */
public abstract class SpecificPart extends AbstractGraphicalEditPart

implements PropertyChangeListener
{

/** *//**
* 将自己注册为模型的属性修改事件的接收者
*/

public void activate()
{

if (!isActive())
{
super.activate();
((ElementModel) getModel()).addPropertyChangeListener(this);
}
}

/** *//**
* 将自己从监听器的列表中移除
*/

public void deactivate()
{
super.deactivate();
((ElementModel)getModel()).removePropertyChangeListener(this);
}

}
然后是shape模型对应的part类
public class ShapeEditPart extends SpecificPart implements NodeEditPart
{

//连接锚点
private ConnectionAnchor anchor;

/** *//**
*@Override
* 建立视图
* */

protected IFigure createFigure()
{
IFigure f = createFigureByGetModel();
f.setOpaque(true); // non-transparent figure
f.setBackgroundColor(ColorConstants.red);
return f;
}


/** *//**根据得到的模型实例,画出不同的图形*/

private IFigure createFigureByGetModel()
{

if (getModel() instanceof RadioModel)
{
return new Ellipse();//圆形

} else if (getModel() instanceof RectangularModel)
{
return new RectangleFigure();//矩形

} else if (getModel() instanceof TriangleModel)
{
return new Triangle();//三角形

} else
{
throw new IllegalArgumentException();
}
}


/** *//**安置策略*/

protected void createEditPolicies()
{
//安装删除策略
installEditPolicy(EditPolicy.COMPONENT_ROLE, new ShapeComponentEditPolicy());
//安装建立,更改连接策略
installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new ShapeConnectionEditPolicy());
}

/** *//**
* @Override
* 刷新视图
* */

protected void refreshVisuals()
{
Rectangle bounds = new Rectangle(((ShapeModel) getModel()).getLocation(),
((ShapeModel) getModel()).getSize());
((GraphicalEditPart) getParent()).setLayoutConstraint(this, getFigure(), bounds);
}


/** *//**
*接受修改事件,刷新视图
* */

public void propertyChange(PropertyChangeEvent arg0)
{
String p = arg0.getPropertyName();

if (ShapeModel.SIZE_PROP.equals(p) || ShapeModel.LOCATION_PROP.equals(p))
{
refreshVisuals();

}else if (ShapeModel.SOURCE_CONNECTIONS_PROP.equals(p))
{
refreshSourceConnections();

} else if (ShapeModel.TARGET_CONNECTIONS_PROP.equals(p))
{
refreshTargetConnections();
}

}
//------------------------------------------------------------------------
//---
//连接设置
//得到相应图形的锚点

protected ConnectionAnchor getConnectionAnchor()
{

if (anchor == null)
{
if (getModel() instanceof RadioModel)
anchor = new EllipseAnchor(getFigure());
else if (getModel() instanceof RectangularModel)
anchor = new ChopboxAnchor(getFigure());
else if(getModel()instanceof TriangleModel)
anchor = new ChopboxAnchor(getFigure());
else
throw new IllegalArgumentException("unexpected");
}
return anchor;
}

//得到以模型作为源的连接列表

protected List getModelSourceConnections()
{
return ((ShapeModel) getModel()).getSourceConnections();
}

//得到以模型作为目标的连接列表

protected List getModelTargetConnections()
{
return ((ShapeModel) getModel()).getTargetConnections();
}

//当需要画连接的时候,获取连接的源锚点

public ConnectionAnchor getSourceConnectionAnchor(ConnectionEditPart connection)
{
return getConnectionAnchor();
}
//准备创建连接的时候,通过Request来获取新连接的源锚点

public ConnectionAnchor getSourceConnectionAnchor(Request request)
{
return getConnectionAnchor();
}
//获取连接的目标锚点

public ConnectionAnchor getTargetConnectionAnchor(ConnectionEditPart connection)
{
return getConnectionAnchor();
}


public ConnectionAnchor getTargetConnectionAnchor(Request request)
{
return getConnectionAnchor();
}

}
由于图形之间是可以连接的所以我们还同时让其实 NodeEditPart 接口。 通过实现这个接口,编辑部件可以定义源锚点和目标锚点,锚点就是图形和连接接触的连接点 。重写了getModelSourceConnections方法和getModelTargetConnections方法。这两个方法的任务就是要通知GEF有关该图形的源连接和目标连接 createFigure()方法,根据返回的对象不同,返回 不同的图形实例(矩形,三角形和圆形)。主要通过方法createFigureByGetModel()来判断。
createEditPolicies()安装了两个策略,ShapeComponentEditPolicy提供命 令将一个 图形从图删除。第二个策略处理图形间连接的创建和转移,它的索引字是 GRAPHICAL_NODE_ROLE。
其他的两个模型对应的part也差不多
DiagamEditPart
package hya.gef.demo.shapes.parts;

import hya.gef.demo.shapes.models.DiagramModel;
import hya.gef.demo.shapes.parts.policy.ShapesXYEditPolicy;

import java.beans.PropertyChangeEvent;
import java.util.List;

import org.eclipse.draw2d.ConnectionLayer;
import org.eclipse.draw2d.Figure;
import org.eclipse.draw2d.FreeformLayer;
import org.eclipse.draw2d.FreeformLayout;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.MarginBorder;
import org.eclipse.draw2d.ShortestPathConnectionRouter;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.LayerConstants;
import org.eclipse.gef.editpolicies.RootComponentEditPolicy;



public class DiagamEditPart extends SpecificPart
{


/** *//**
*@Override
* 建立视图
* 建立容器,设置布局管理器
* */

protected IFigure createFigure()
{
Figure f = new FreeformLayer();
f.setBorder(new MarginBorder(3));
//FreeformLayout布局管理器,这是一种XY型的布局管理器
f.setLayoutManager(new FreeformLayout());

// Create the static router for the connection layer
ConnectionLayer connLayer = (ConnectionLayer)getLayer(LayerConstants.CONNECTION_LAYER);
connLayer.setConnectionRouter(new ShortestPathConnectionRouter(f));
return f;
}


/** *//**
* 得到子对象列表
* */

protected List getModelChildren()
{
return ((DiagramModel) getModel()).getChildren(); // return a list of shapes
}



protected void createEditPolicies()
{
//阻止模型的根被删除,它重写了createDeleteCommand方法,并返回一个不能被执行的命令
installEditPolicy(EditPolicy.COMPONENT_ROLE, new RootComponentEditPolicy());
//安装添加和更改元素策略
installEditPolicy(EditPolicy.LAYOUT_ROLE, new ShapesXYEditPolicy());

}


/** *//**模型改变时,刷新视图*/

public void propertyChange(PropertyChangeEvent arg0)
{
String p = arg0.getPropertyName();
if (DiagramModel.CHILD_ADDED_PROP.equals(p)

|| DiagramModel.CHILD_REMOVED_PROP.equals(p))
{
refreshChildren();
}

}

}
ConnectionEditPart
package hya.gef.demo.shapes.parts;

import hya.gef.demo.shapes.commands.ConnectionDeleteCommand;
import hya.gef.demo.shapes.models.Connection;


import org.eclipse.draw2d.BendpointConnectionRouter;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.PolygonDecoration;
import org.eclipse.draw2d.PolylineConnection;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editparts.AbstractConnectionEditPart;
import org.eclipse.gef.editpolicies.ConnectionEditPolicy;
import org.eclipse.gef.editpolicies.ConnectionEndpointEditPolicy;
import org.eclipse.gef.requests.GroupRequest;


class ConnectionEditPart extends AbstractConnectionEditPart
{




protected void createEditPolicies()
{
//安装与连接有关的策略
installEditPolicy(EditPolicy.CONNECTION_ENDPOINTS_ROLE,
new ConnectionEndpointEditPolicy());

installEditPolicy(EditPolicy.CONNECTION_ROLE, new ConnectionEditPolicy()
{

protected Command getDeleteCommand(GroupRequest request)
{
return new ConnectionDeleteCommand(getCastedModel());
}
});
}



protected IFigure createFigure()
{
PolylineConnection conn = new PolylineConnection();
conn.setTargetDecoration(new PolygonDecoration());
conn.setConnectionRouter(new BendpointConnectionRouter());
return conn;
}




private Connection getCastedModel()
{
return (Connection) getModel();
}


} 连接编辑部件,它继承自AbstractConnectionEditPart类。createFigure()方法返回了一个带箭头的连接线。
他安装了两个策略第一个是ConnectionComponentPolicy,它提供删除命令给Delete菜单项 所需要的action。第二个策略提供了一种视觉上的选择反馈,即当一个连接被拖动时,GEF 没有办法获取连接两端的标识.
那么GEF是怎么知道模型与PART之间的对应关系的呢,所以应该建立模型与part之间的映射,这个我们用到了工厂模式
建立part工厂:ShapesEditPartFactory
package hya.gef.demo.shapes.parts;

import hya.gef.demo.shapes.models.Connection;
import hya.gef.demo.shapes.models.DiagramModel;
import hya.gef.demo.shapes.models.ShapeModel;

import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPartFactory;




/** */ /**
* 控制器工厂
* @author hya
* */

public class ShapesEditPartFactory implements EditPartFactory
{



public EditPart createEditPart(EditPart context, Object modelElement)
{
EditPart part = getPartForElement(modelElement);
part.setModel(modelElement);
return part;
}


/** *//**建立模型与EditPart的映射*/

private EditPart getPartForElement(Object modelElement)
{

if (modelElement instanceof DiagramModel)
{
return new DiagamEditPart();
}

if (modelElement instanceof ShapeModel)
{
return new ShapeEditPart();
}

if (modelElement instanceof Connection)
{
return new ConnectionEditPart();
}
throw new RuntimeException(
"Can't create part for model element: "
+ ((modelElement != null) ? modelElement.getClass().getName() : "null"));
}

} 这是,核心的part就建立完了.我们可以看到他们当中的方几乎一样,狭义回我们将加入策略和命令部分,并更加详细的通过代码来说明GEF的工作过程.