转自:http://blog.csdn.net/li_canhui/article/details/6798380
先看看gef进阶的router部分:http://www.ibm.com/developerworks/cn/opensource/os-ecl-gef/part2/index.html
ConnectionRouter(连线路由器)
图形之间连线的路线,是由连线路由器来决定的。在Shapes Examples中,使用了最短路径路由器,这个路由器会帮我们绕开图形之间的障碍,选择一条最短路径进行连接,如图1所示:
图1. ShortestPathConnectionRouter效果图
我们看到左右两边图形的连线绕过了中间的图形,在两处发生了转折。这就是使用了最短路径路由器的效果。连线路由器可以安装到Connection Layer(连接层,关于层的概念我们在本系列下一篇中讲述),也可以针对某一条连接,所以只要你愿意,每条连线都可以有不同的路由器。如果你没有为某条连线指定一个路由器,那么缺省会使用连接层的路由器。Draw2D自带了一些路由器的实现,除了图1的ShortestPathConnectionRouter,还有ManhattanConnectionRouter等路由器实现,如果这些自带的路由器不能满足我们的需要,我们所要做的就是实现ConnectionRouter接口,实现一个自定义路由器。ConnectionRouter接口并不复杂,如下所示:
- Object getConstraint(Connection connection);
- void setConstraint(Connection connection, Object constraint);
- void invalidate(Connection connection);
- void route(Connection connection);
- void remove(Connection connection);
setConstraint和getConstraint用来设置/得到连接上的Constraint(约束),所谓Constraint是指加在某个连线上的一些参数。我们可以看到constraint是一个Object类型,因为不同的路由器可能对constraint有不同的要求,对于ShortestPathConnectionRouter来说,constraint需要是一个List对象,里面包含了所有的转折点。
invalidate方法可以将一个连线置为无效,这样在下一次布局操作时,无效的连接将被重新路由。
remove方法是将连线从路由器中删除,也就是路由器不会再负责这条连线的布局,一般只有在删除一条连线的时候才会调用到,我们可以在里面做一些清除工作,比如释放和连线相关的cache。
route方法是路由操作真正发生的地方,我们一般只需要实现route方法就可以了,如果你还想做一些其他的操作,可以考虑实现其他方法。同样,一般是不推荐直接实现ConnectionRouter接口的,我们可以继承AbstractRouter类,这个类提供了一些简单的或者空的实现,还提供了两个额外的方法getStartPoint()和getEndPoint()方便我们得到连线的两个端点。
SingleBendpointConnectionRouter
我们将实现一个自定义的路由器,叫做SingleBendpointConnectionRouter,它采用一种走直角的方式连接两个图形,如图2所示:
图2. SingleBendpointConnectionRouter效果图
路由器实现的前提
显然我们无法凭空的计算出线路的走向,一条连线的具体路线和很多因素有关,比如锚点、图形的位置和大小,图形之间的相互关系,等等。所以我们需要能够访问到这些必须的信息,在Connection接口中,我们有getTargetAnchor()和getSourceAnchor()可以让我们得到锚点,而在ConnectionAnchor接口中(参见本系列第一部分),我们有getOwner()这样的方法,可以得到图形。这些必要的方法为我们实现路由器提供了可能。
实现route方法
route方法的代码如下:
1 public void route(Connection conn) { 2 // 清空连线的所有点 3 PointList points = conn.getPoints(); 4 points.removeAllPoints(); 5 6 // 得到目标和源参考点 7 Point sourceRef = conn.getSourceAnchor().getReferencePoint(); 8 Point targetRef = conn.getTargetAnchor().getReferencePoint(); 9 A_POINT.setLocation(sourceRef.x, targetRef.y); 10 11 // 得到起始点和结束点 12 Point startPoint = conn.getSourceAnchor().getLocation(A_POINT); 13 Point endPoint = conn.getTargetAnchor().getLocation(A_POINT); 14 15 // 添加起始点 16 A_POINT.setLocation(startPoint); 17 conn.translateToRelative(A_POINT); 18 points.addPoint(A_POINT); 19 20 // 添加转折点 21 A_POINT.setLocation(sourceRef.x, targetRef.y); 22 conn.translateToRelative(A_POINT); 23 points.addPoint(A_POINT); 24 25 // 添加结束点 26 A_POINT.setLocation(endPoint); 27 conn.translateToRelative(A_POINT); 28 points.addPoint(A_POINT); 29 30 // 设置连线经过的所有点 31 conn.setPoints(points); 32 }
在我们这个路由器的设计里,一条连线由三个点组成:分别是起始点,转折点和结束点,它们构成了两条垂直的直线。起始点和结束点(也就是锚点)我们都已经了解如何得到了,中间的转折点,也很容易得出,我们就不解释了。要指出的是,我们需要把它们的坐标转换为相对坐标再添加,同时在添加完成之后,我们还需要调用setPoints()方法,这样才会生效。
所以说实现一个路由器的过程是很简单的,复杂之处在于路由算法。
改变连接层的路由器
我们只是实现了路由器,还没有把这个路由器设置为缺省的路由器,所以我们还要做一点小修改,在DiagramEditPart的createFigure()方法里,将ShortestPathConnectionRouter替换为SingleBendpointConnectionRouter即可。
待改进的地方
我们的自定义路由器很简单,但是它也有一点小问题,当两个图形在垂直或水平方向有重叠时,连线看上去有点不正常,如图3所示:
图3. 一点小bug
这只是由于我们的路由器算法不是很完善,没有考虑到所有情况而已。需要改进路由算法。
所以总结下:
1.创建新类:SingleBendpointConnectionRouter
2.在connectionEditPart的createFigure()方法里,将ShortestPathConnectionRouter替换为SingleBendpointConnectionRouter。
常见的 ConnectionRouter 为 BendpointConnectionRouter, FanRouter,ConnectionRouter.NullConnectionRouter,ManhattanConnectionRouter, ShortestPathConnectionRouter。
setConnectionRouter(ConnectionRouter cr)
所以重点在于自定义的router类里面的东西。
to be continued。