GEF功能增强之连接助手
GEF图形编辑器中,通常会有连接线的存在,如果没有,这篇文章你有兴趣也可以看看。
要连接两个图元,通常我们的作法是:选择palette中的connectionTool,然后选择一个图,再拖去鼠标到另一个图元上去。
我看的有editor会有一些快捷方式,支持我称之为连接助手的功能:选择一个图元,在图元的右下角会长出一些功能图元,选择其中一个(例如connection的功能),就可以直接拖动到其他图上,从而完成连接的操作。看附件图。
这样的一个方便之处就是省了在palette上的切换的操作,当然推而广之,可以将某一类模型具体的所有操作都添加到这个功能区域,从而实现把操作集成到这个模型对象上。
实现这样的功能首先之一是:怎么让功能图显示出来。
首先要明确一点:这些功能图不是本身图元的一部分,所以不要所它直接画在宿主图上。我们可以参考handler,例如当选中一个图元的时候,会出现一个selection的handler,它会围绕着图元出现,但不属于这个图元,如果知道gef中层的概念的话就知道,handler是放在handler层,类似的还有feedback放在feedback层。
所以参考selectionHandler,我们也可以实现一个类似的handler,所以剩下的就是怎么实现handler的问题了。
在LayoutEditPolicy里有一个createChildEditPolicy(EditPart),是用来给child添加装饰用的。看它的子类实现就可以看到,ConstrainedLayoutEditPolicy里返回了一个ResizableEditPolicy,里面有一个createSelectionHandles()方法,打个断点就可以看到,当选择这个editpart的时候会调到这个方法。
所以简单一点的实现就是给你的Editpart安装一个LayoutEditPolicy的实现,重写它的createChildEditPolicy()方法,返回一个自定义的ResizableEditPolicy。
在这个自定义的ResizableEditPolicy里重写createSelectionHandles()方法,例如:
@Override
protected EditPolicy createChildEditPolicy(EditPart child) {
return new ConnectionHelperEditPolicy();
}
public class ConnectionHelperEditPolicy extends ResizableEditPolicy {
@Override
protected List createSelectionHandles() {
List selectionHandles = super.createSelectionHandles();
if (getHost() instanceof ***EditorPart) {
selectionHandles.add(new ConnectionHelperHandler(
(GraphicalEditPart) getHost()));
selectionHandles.add(new ArrowConnectionHelperHandler(
(GraphicalEditPart) getHost()));
}
return selectionHandles;
}
}
这样,当你选中图元的时候,除了selectionHandles,还会多出两个handle,就像附件图里看到的。
后面的就是怎么实现这两个HelperHandler了。这里我合成一个讲,因为我的类子里,这两个大同小异,只不过一个是带前头的连线,一个不带箭头。
每一个handle都是Handle类的一个实现,GEF里已经提供了一大堆的实现,你可以根据需要选择继承某个类。这里我选择继承SquareHandle。
这里有几个需要实现的东西:
1.Locator
提供一个定位器显然是需要的,否则这个handle就不知道要安在哪了。
2.实现paintFigure()方法
如果你的继承SquareHandle,默认会是一个方框,如果你觉得不合适,那就需要自己实现这个方法了,就像附件图里。
3.实现createDragTracker()方法
这个方法返回一个DragTracker,就是当你拖动鼠标时的处理类。
4.其他功能
1):可以增加一些美观性的东西,例如鼠标滑过时视觉加深,鼠标滑出时变淡。
2):如果你的Editor支持放大缩小功能,那你还得处理缩放时的动作,否则当有缩放时,你的handle的位置可能就不对了。
提醒:看附件图中可以知道,这两个handle的位置已经超出图元的范围,所以当你拖运鼠标时提供的startLocation就是不对的了,已经超出图的范围了。所以我们需要在DragTracker里按某个算法提供自己的StartLocation。例如:
protected Point getStartLocation() {
return ((GraphicalEditPart) target).getFigure().getBounds()
.getLocation();
}
protected boolean handleButtonDown(int button) {
Point startLocation = getStartLocation();
getCurrentInput().setMouseLocation(startLocation.x, startLocation.y);
return super.handleButtonDown(button);
}
实话说,我不知道是不是必须这样给出位置,还是有其他更好的定位初始位置的方法。
最后完成上面的实现后,你就可以通过拖动图中不动的handle来分别实现有箭头连接或无箭头连接。看下图,palette中的connectionTool并没有被选中,但已经可以创建连接线了(鼠标没有被截出来),从高亮可以看出现在是在画有前头图