對於DirectedGraph以及DirectedGraphLayout的一點思索

來源:互聯網
上載者:User

凡是研究過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更是讓我一頭霧水,這個類到底是幹什麼的呢?最後經過多方尋找,並Google了半天,才知道它們被用到的底層圖形的LayoutManager中。
做GEF的,用腳後跟都知道,“圖形工作區”本身是需要一個EditPart的,而這個作為Controller的EditPart是需要一個我稱之為底層圖形的Figure的,這個Figure是在EditPart的createFigure方法中產生的,在Flow例子中也不例外,通過源碼我們可以看出,這個Figure不像我們普通的GEF應用那樣使用了XYLayout,而是使用了自訂的GraphLayoutManager。
代碼

 1 protected IFigure createFigure() {
 2     Figure f = new Figure() {
 3         public void setBounds(Rectangle rect) {
 4             int x = bounds.x, y = bounds.y;
 5             boolean resize = (rect.width != bounds.width) || (rect.height != bounds.height), translate = (rect.x != x) || (rect.y != y);
 6 
 7             if (isVisible() && (resize || translate))
 8                 erase();
 9             if (translate) {
10                 int dx = rect.x - x;
11                 int dy = rect.y - y;
12                 primTranslate(dx, dy);
13             }
14             bounds.width = rect.width;
15             bounds.height = rect.height;
16             if (resize || translate) {
17                 fireFigureMoved();
18                 repaint();
19             }
20         }
21     };
22     f.setLayoutManager(new GraphLayoutManager(this));
23         
24     return 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的廬山真面目吧:

代碼

 1 public final class CompoundDirectedGraphLayout extends DirectedGraphLayout {
 2 
 3     void 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的建立;分離,指責分離,解耦這恐怕是程式的最高境界了吧,看看高手是怎麼做的。唉,我還差的遠哪!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.