GEF原理及实现系列(四、控制器)

控制器是GEF框架的核心,它负责模型和视图之间的通信。
1.控制器的功能
在GEF的MVC结构里,控制器是模型与视图之间的桥梁,也是整个GEF的核心,它不仅要监听模型的变化,当用户编辑视图时,还要把编辑结果反应到模型上。
在GEF中,控制器是由一组EditPart对象共同组成的,每一个模型对象都对应一个EditPart对象。应用程序中需要有一个EditPartFactory对象负责根据给定模型对象创建对应的EditPart对象,这个工厂类将在创建模型时被调用。
控制器是GEF中最复杂的一部分,GEF把控制器完成的工作又分成了几个部分,包括请求和编辑策略及引申出来的命令模式,如下图:

用户的编辑操作被转换为一系列请求(Request),Eclipse中有很多种类的请求,这些种类在GEF里被成为角色(Role)。在GEF里有图形 化和非图形化这两大类角色,前者如“LayoutRole”对应和布局有关的操作,后者如“ConnectionRole”对应和连接有关的操作等。角色 这个概念是通过编辑策略(EditPolicy)来实现的,EditPolicy的主要功能是根据请求创建相应的命令(Command),而后者会直接操 作模型对象。
对每一个EditPart,用户都可以“安装”一些EditPolicy。用户对这个EditPart的特定操作会被交给已安装的对应EditPolicy处理。这样做的直接好处是可以在不同EditPart之间共享一些重复操作。
2.控制器的实现
        每一个模型都会对应一个控制器,控制器主要负责模型和视图的同步。控制器要监听模型的属性改变的事件,并通知视图更新。另外,控制器还要设置相应的编辑策略,用来处理由视图接收的请求。
实现一个EditPart一般来说需要重载performRequest、getCommand、 activate、deactivate和refreshVisuals函数,并实现createEditPolicies、createFigure接 口函数,为了让EditPart能够成为PropertyChangeListener,你还必须实现PropertyChangeListener接 口。performRequest和getCommand将在第3节介绍,activate和deactivate用于处理当EditPart处于激活或 者非激活状态时的操作,一般来说,可以在这两个函数中注册和移除自己监听者的角色。refreshVisuals函数用于更新自己的视图。
java 代码
package com.example.parts; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.List; import org.eclipse.draw2d.ChopboxAnchor; import org.eclipse.draw2d.ConnectionAnchor; import org.eclipse.draw2d.IFigure; import org.eclipse.draw2d.geometry.Dimension; import org.eclipse.draw2d.geometry.Point; import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.gef.ConnectionEditPart; import org.eclipse.gef.EditPolicy; import org.eclipse.gef.GraphicalEditPart; import org.eclipse.gef.NodeEditPart; import org.eclipse.gef.Request; import org.eclipse.gef.RequestConstants; import org.eclipse.gef.editparts.AbstractGraphicalEditPart; import org.eclipse.gef.tools.DirectEditManager; import org.eclipse.jface.viewers.TextCellEditor; import com.example.figures.NodeFigure; import com.example.model.Node; import com.example.policies.NodeDirectEditPolicy; import com.example.policies.NodeEditPolicy; import com.example.policies.NodeGraphicalNodeEditPolicy; public class NodePart extends AbstractGraphicalEditPart implements PropertyChangeListener, NodeEditPart { protected DirectEditManager manager; //处理请求 public void performRequest(Request req) { if (req.getType().equals(RequestConstants.REQ_DIRECT_EDIT)) { if (manager == null) { NodeFigure figure = (NodeFigure) getFigure(); manager = new NodeDirectEditManager(this, TextCellEditor.class, new NodeCellEditorLocator(figure)); } manager.show(); } } //监听模型属性改变 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(); } //创建模型对应的视图 protected IFigure createFigure() { return new NodeFigure(); } //设置编辑策略 protected void createEditPolicies() { installEditPolicy(EditPolicy.DIRECT_EDIT_ROLE, new NodeDirectEditPolicy()); installEditPolicy(EditPolicy.COMPONENT_ROLE, new NodeEditPolicy()); installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new NodeGraphicalNodeEditPolicy()); } //注册自己成为模型的监听器 public void activate() { if (isActive()) { return; } super.activate(); ((Node) getModel()).addPropertyChangeListener(this); } //把自己从模型的监听器中删除 public void deactivate() { if (!isActive()) { return; } super.deactivate(); ((Node) getModel()).removePropertyChangeListener(this); } //更新视图的显示 protected void refreshVisuals() { Node node = (Node) getModel(); Point loc = node.getLocation(); Dimension size = new Dimension(150, 40); Rectangle rectangle = new Rectangle(loc, size); ((NodeFigure) this.getFigure()).setName(((Node) this.getModel()).getName()); ((GraphicalEditPart) getParent()).setLayoutConstraint(this, getFigure(), rectangle); } //------------------------------------------------------------------------ // Abstract methods from NodeEditPart //得到源连接的连接点 public ConnectionAnchor getSourceConnectionAnchor(ConnectionEditPart connection) { return new ChopboxAnchor(getFigure()); } public ConnectionAnchor getSourceConnectionAnchor(Request request) { return new ChopboxAnchor(getFigure()); } //得到目标连接的连接点 public ConnectionAnchor getTargetConnectionAnchor(ConnectionEditPart connection) { return new ChopboxAnchor(getFigure()); } public ConnectionAnchor getTargetConnectionAnchor(Request request) { return new ChopboxAnchor(getFigure()); } //得到以模型作为源的连接列表 protected List getModelSourceConnections() { return ((Node) this.getModel()).getOutgoingConnections(); } //得到以模型作为目标的连接列表 protected List getModelTargetConnections() { return ((Node) this.getModel()).getIncomingConnections(); } }
EditPart(确切的说是AbstractGraphicalEditpart)另外一个需要实现的重要方法是 createFigure(), 这个方法应该返回模型在视图中的图形表示,是一个IFigure类型对象。一般都把这些图形放在figures包里,例子里只有NodeFigure一个 自定义图形,Diagram对象对应的是GEF自带的名为 FreeformLayer的图形,它是一个可以在东南西北四个方向任意扩展的层图形;而 Connection对应的也是GEF自带的图形,名为 PolylineConnection,这个图形缺省是一条用来连接另外两个图形的直线,在例子里 我们通过 setTargetDecoration()方法让连接的目标端显示一个箭头

最后,要为EditPart增加适当的EditPolicy,这是通过覆盖EditPart的createEditPolicies()方法来实现 的,每一个被"安装"到EditPart中的EditPolicy都对应一个用来表示角色(Role)的字符串。对于在模型中有子元素的 EditPart,一般都会安装一个EditPolicy.LAYOUT_ROLE角色的EditPolicy(见下面的代码),后者多为 LayoutEditPolicy的子类;对于连接类型的EditPart,一般要安装 EditPolicy.CONNECTION_ENDPOINTS_ROLE角色的EditPolicy,后者则多为 ConnectionEndpointEditPolicy或其子类,等等。

installEditPolicy(EditPolicy.LAYOUT_ROLE, new DiagramLayoutEditPolicy());

用户的操作会被当前工具(缺省为选择工具SelectionTool)转换为请求(Request),请求根据类型被分发到目标EditPart所安装的EditPolicy,后者根据请求对应的角色来判断是否应该创建命令并执行。

    在GEF里,这个弹出的编辑器由DirectEditManager类负责管理,在我们的NodePart类里,通过覆盖 performRequest()方法响应用户的DirectEdit请求,在这个方法里一般要构造一个DirectEditManager类的实例(例 子中的NodeDirectEditManager),并传入必要的参数,包括接受请求的EditPart(就是自己,this)、编辑器类型(使用 TextCellEditor)以及用来定位编辑器的CellEditorLocator(NodeCellEditorLocator),然后用 show()方法使编辑器显示出来,而编辑器中显示的内容已经在构造方法里得到。简单看一下NodeCellEditorLocator类,它的关键方法 在relocate()里,当编辑器里的内容改变时,这个方法被调用从而让编辑器始终处于正确的坐标位置。DirectEditManager有一个重要 的initCellEditor()方法,它的主要作用是设置编辑器的初始值。在我们的例子里,初始值设置为被编辑NodePart对应模型 (Node)的name属性值;这里还另外完成了设置编辑器字体和选中全部文字(selectAll)的功能,因为这样更符合一般使用习惯。

        在NodePart里还要增加一个角色为DIRECT_EDIT_ROLE的EditPolicy,它应该继承自 DirectEditPolicy,有两个方法需要实现:getDirectEditCommand()和showCurrentEditValue (),虽然还未遇到过,但前者的作用你不应该感到陌生--在编辑结束时生成一个Command对象将修改结果作用到模型;后者的目的是更新Figure中 的显示,虽然我们的编辑器覆盖了Figure中的文本,似乎并不需要管Figure的显示,但在编辑中时刻保持这两个文本的一致才不会出现"盖不住"的情 况,例如当编辑器里的文本较短时。

        上例通过继承AbstractGraphicalEditPart类实现了一个EditPart,另外,当GEF框架创建一个模型时,都会为此模型创建一 个对应的控制器。创建控制器的操作是在控制器工厂中完成的,用户可以在编辑器初始化时指定控制器工厂,例如:“getGraphicalViewer ().setEditPartFactory(new PartFactory())”,其中PartFactory为控制器工厂,代码如下:
java 代码
/* * Created on 2005-1-24 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */ package com.example.parts; import org.eclipse.gef.EditPart; import org.eclipse.gef.EditPartFactory; import com.example.model.Connection; import com.example.model.Diagram; public class PartFactory implements EditPartFactory { public EditPart createEditPart(EditPart context, Object model) { EditPart part = null; //根据模型创建相应的EditPart if (model instanceof Diagram) part = new DiagramPart(); else if (model instanceof Connection) part = new ConnectionPart(); else part = new NodePart(); //设置EditPart对应的模型 part.setModel(model); //返回根据模型创建的EditPart return part; } }

通过创建控制器和控制器工厂,GEF编辑器将会知道在模型创建后,自动调用控制器工厂创建对应的空气,从而实现模型和控制器的一一对应关系。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值