对于DirectedGraph以及DirectedGraphLayout的一点思索
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
对于DirectedGraph以及DirectedGraphLayout的⼀点思索
凡是研究过GEF的例⼦Flow的,都应该知道这个例⼦是可以⾃动布局的,当向“画图⼯作区”添加⼀些图形元素的时候,程序将会⾃动布局,并以动画的形式表⽰出来。
这个功能看起来很简单,但是实际上却包含了很多需要我们考虑的内容。
起初,我对其中的动画部分很感兴趣,⾮常希望了解更多的实现动画的⽅式,因为我就是⼀个俗⼈,我只想知道怎么才能让程序如此之炫,但是直⾄深⼊研究下去才发现,原来还有⽐这更有意思的东西存在,当然这是以往我所不知道的,这⾥我把它们写出来,算是⼀个⾃我总结吧。
问题出现在查看例⼦中各个EditPart源码的过程中,我发现在⼏个EditPart的源码中总是会出现⼏个⽅法:contributeEdgesToGraph(CompoundDirectedGraph graph, Map map);
contributeNodesToGraph(CompoundDirectedGraph graph, Subgraph s, Map map);
applyGraphResults(CompoundDirectedGraph graph, Map map);
applyChildrenResults(CompoundDirectedGraph graph, Map map);
说实话,起初我真的不明⽩这⼏个⽅法究竟是⼲嘛的,这些⽅法都是孤⽴的⽅法,EditPart中其他⽅法并没有对它们进⾏使⽤,不仅如此,这些⽅法中还有⼀个奇怪的参数CompoundDirectedGraph更是让我⼀头雾⽔,这个类到底是⼲什么的呢?最后经过多⽅查找,并⾕歌了半天,才知道它们被⽤到的底层图形的LayoutManager中。
做GEF的,⽤脚后跟都知道,“图形⼯作区”本⾝是需要⼀个EditPart的,⽽这个作为Controller的EditPart是需要⼀个我称之为底层图形的Figure的,这个Figure是在EditPart的createFigure⽅法中⽣成的,在Flow例⼦中也不例外,通过源码我们可以看出,这个Figure不像我们普通的GEF应⽤那样使⽤了XYLayout,⽽是使⽤了⾃定义的GraphLayoutManager。
代码
1protected IFigure createFigure() {
2 Figure f = new Figure() {
3public void setBounds(Rectangle rect) {
4int x = bounds.x, y = bounds.y;
5boolean resize = (rect.width != bounds.width) || (rect.height != bounds.height), translate = (rect.x != x) || (rect.y != y);
6
7if (isVisible() && (resize || translate))
8 erase();
9if (translate) {
10int dx = rect.x - x;
11int dy = rect.y - y;
12 primTranslate(dx, dy);
13 }
14 bounds.width = rect.width;
15 bounds.height = rect.height;
16if (resize || translate) {
17 fireFigureMoved();
18 repaint();
19 }
20 }
21 };
22 f.setLayoutManager(new GraphLayoutManager(this));
23
24return f;
25 }
26
请注意第22⾏,由此可见底层图形的布局管理就着落在这个⾃定义的LayoutManager上了。
那么既然是⼀个⾃定义的LayoutManager,那么就让我们见识见识它的庐⼭真⾯⽬吧:
代码
class GraphLayoutManager extends AbstractLayout {
private ActivityDiagramPart diagram;
GraphLayoutManager(ActivityDiagramPart diagram) {
this.diagram = diagram;
}
/**
* 这⾥的这个container就是安装了这个布局管理器的那个EditPart所对应的Figure
* 在这⾥,它就是diagram这个EditPart对应的底层图形元素
*/
protected Dimension calculatePreferredSize(IFigure container, int wHint, int hHint) {
container.validate();
List children = container.getChildren();
Rectangle result = new Rectangle().setLocation(container.getClientArea().getLocation());
for (int i = 0; i < children.size(); i++)
result.union(((IFigure) children.get(i)).getBounds());
result.resize(container.getInsets().getWidth(), container.getInsets()
.getHeight());
return result.getSize();
}
public void layout(IFigure container) {
GraphAnimation.recordInitialState(container);
if (GraphAnimation.playbackState(container))
return;
CompoundDirectedGraph graph = new CompoundDirectedGraph();
Map partsToNodes = new HashMap();
diagram.contributeNodesToGraph(graph, null, partsToNodes);
diagram.contributeEdgesToGraph(graph, partsToNodes);
new CompoundDirectedGraphLayout().visit(graph);
diagram.applyGraphResults(graph, partsToNodes);
}
}
呀,怎么这么简单。
呵呵,不然。
⾸先,我们看到了上⾯我们说过的两个不知所谓的⽅法,由此证明这些⽅法是和布局有关的,但是我们还发现,这⾥⾯还有⼀个⽅法new CompoundDirectedGraphLayout().visit(graph);这⼜是何⽅神圣?为啥弄个visit的⽅法名,莫⾮...
当然,答案是肯定的,Flow例⼦在布局管理器上使⽤了访问者模式,我跪倒在地了,我还能说什么,世界上就是有⼀些⼈⽣下来就⽐我强,不知道各位如何,也许你们会对此嗤之以⿐,会说访问者模式谁不会⽤,还⽤说么?但是我不会这样,我只有佩服,Eclipse真的是⼀座宝库,⾥⾯需要我们理解和学习的东西太多了。
那么我们就看看CompoundDirectedGraphLayout的庐⼭真⾯⽬吧:
代码
1public final class CompoundDirectedGraphLayout extends DirectedGraphLayout {
2
3void init() {
4 steps.add(new TransposeMetrics());
5 steps.add(new CompoundBreakCycles());
6 steps.add(new RouteEdges());
7 steps.add(new ConvertCompoundGraph());
8 steps.add(new InitialRankSolver());
9 steps.add(new TightSpanningTreeSolver());
10 steps.add(new RankAssignmentSolver());
11 steps.add(new CompoundPopulateRanks());
12 steps.add(new CompoundVerticalPlacement());
13 steps.add(new MinCross(new CompoundRankSorter()));
14 steps.add(new SortSubgraphs());
15 steps.add(new CompoundHorizontalPlacement());
16 }
17
18 }
19
这⾥⾯steps增加的每⼀个元素都是⼀个visitor,每⼀个visitor都是GraphVisitor的⼀个⼦类,它们⼯作总的来说就是对给定的DirectedGraph 进⾏修改,从上⾯给出的那么多visitor中,从字⾯上可以看出有“对边进⾏路由”,“减少边的交叉”,“垂直摆放”,“⽔平摆放”等等。
当然还有好多不能从字⾯上翻译,但是⾄少我们能够看出,布局管理器⾸先将底层图形及其包含的所有⼦图形转化为⼀个DirectedGraph,然后通过访问者模式将相应的DirectedGraph进⾏了修改,然后再把修改后的结果“布局出来”,布局的过程是通过动画的形式来实现的。
套⽤平原枪声的⼀句⽼话:⾼,实在是⾼!
到此扯了这么多,那到底啥是DirectedGraph呢?字⾯上,当然是“有向图”,但是如果这么理解可能会更好,那就是:DirectedGraph是⼀个抽象概念的图形,它和Figure不同之处在于,它仅仅维护了概念图形中包含了那些节点、这些节点之间有哪些边(Edge),就像我们学习数据结构时讨论的Graph⼀样,它并不关⼼这些图形应该如何绘制,你愿意把它画到墙上还是画在纸上,以及你怎么画这个图,它完全不管。
为了让它能够正常⼯作必须将它和相应的EditPart -- Viewer结合起来,由DirectedGraph负责概念领域的图形,对应的Figure负责显⽰,⽽EditPart负责Viewer的创建;分离,指责分离,解耦这恐怕是程序的最⾼境界了吧,看看⾼⼿是怎么做的。
唉,我还差的远哪!。