首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

GEF 入門系列(三):應用實例完整解析

     編輯:Java 相關知識

構建 GEF 應用程序通常分幾步:設計模型、設計 EditPart 和 Figure、設計 EditPolicy 和 Command,其中 EditPart 是最主要部分,因實現它時不可避免用到 EditPolicy,後者又涉及 Command。

現在看個例子,功能簡單:用戶可在畫布上增加節點和節點間連接,直接編輯節點名稱、改變節點位置,可撤銷/重做任何操作,有一個樹狀大綱視圖和一個屬性頁。這是一個 Eclipse 專案打包文件,導入後運行 Run-time Workbench,新建擴展名為 "gefpractice" 的文件即打開編輯器。

圖1 Practice Editor 使用界面

可參考代碼看接下來內容。從模型開始:模型根據應用需求設計,包括代表整個圖的 Diagram、代表節點的 Node 和代表連接的 Connection。模型負責把改變通知 EditPart,為把此功能分離,用 Element 抽象類專門實現通知機制,其他模型類繼承它。Element 類含 PropertyChangeSupport 類型成員變數,提供 addPropertyChangeListener()、removePropertyChangeListener() 和 fireXXX() 方法分別用於註冊監聽器和通知模型改變事件。在 GEF 中,模型監聽器即 EditPart,在 EditPart activate() 將它作為監聽器註冊到模型。共四個類組成模型部分。

編輯器實現

前文說過,大部分 GEF 應用實現為 Editor,此例不例外,對應 Editor 為 PracticeEditor。繼承 GraphicalEditorWithPalette,表示具有調色板的圖形編輯器。最重要兩方法:configureGraphicalViewer() 和 initializeGraphicalViewer(),分別用於定制和初始化 EditPartViewer。查看 GEF 代碼發現,GraphicalEditor 會先後調用這兩方法,中間插 hookGraphicalViewer(),作用是同步選擇和把 EditPartViewer 作為 SelectionProvider 註冊到 site。故與選擇無關的初始化操作應在前者完成,否則放在後者。例子中在這兩方法配置了 RootEditPart、創建 EditPart 的 EditPartFactory、Contents 即 Diagram 對象和增加拖放支援,拖動目標是當前 EditPartViewer,拖動源是調色板。

此 Editor 帶調色板,故覆寫 getPaletteRoot() 告訴 GEF 調色板有哪些工具。利用工具類 PaletteFactory 構造 PaletteRoot 返回。調色板需三種工具:選擇工具、節點工具、連接工具。在 GEF 中,調色板可有抽屜(PaletteDrawer)歸類放置工具,每個工具是 ToolEntry。選擇工具(SelectionToolEntry)和連接工具(ConnectionCreationToolEntry)預定義好,可直接用。節點工具用 CombinedTemplateCreationEntry,把節點類型作參數之一傳給它,創建節點工具代碼:

ToolEntry tool = new CombinedTemplateCreationEntry("Node", "Create a new Node",
        Node.class, new SimpleFactory(Node.class), null, null);

GEF 3.0 新提供可自動隱藏調色板的編輯器 GraphicalEditorWithFlyoutPalette,對調色板外觀有更多選項,後續可能提到如何使用。

調色板初始化操作應放在 initializePaletteViewer() 完成,主要任務為調色板所在 EditPartViewer 添加拖動源事件支援。前面已為畫布所在 EditPartViewer 添加拖動目標事件,現可實現完整拖放。稍微講解拖放實現原理,以創建節點對象的節點工具為例,它在調色板是 CombinedTemplateCreationEntry,創建時指定對應 Node.class,用戶從調色板拖動此工具時,內存中 TemplateTransfer 單例記錄 Node.class(稱 template),用戶在畫布鬆開滑鼠時,拖放結束事件觸發,由畫布註冊的 DiagramTemplateTransferDropTargetListener 處理 template(現為 Node.class),範例中用 ElementFactory 對象負責根據 template 創建對應類型實例。

Command 設計

模型改變由 Command 直接修改,故先看有哪些 Command。需求可知,對模型操作有增刪節點、修改節點名稱、改變節點位置、增刪連接,對應有 CreateNodeCommand、DeleteNodeCommand、RenameNodeCommand、MoveNodeCommand、CreateConnectionCommand、DeleteConnectionCommand,歸類在 commands 包。Command 最重要是 execute() 方法。為實現撤銷/重做,Command 有 undo() 和 redo() 方法,同時有成員變數保留執行命令時相關狀態,如 RenameNodeCommand 有 oldName 和 newName,才能正確執行 undo()/redo()。記住,每個被執行過的 Command 實例都保存在 EditDomain 的 CommandStack 中。

EditPolicy 實現

例子 EditPolicy 在 policies 包。與圖形相關的(GraphicalEditPart 子類)有 DiagramLayoutEditPolicy、NodeDirectEditPolicy、NodeGraphicalNodeEditPolicy,另外兩個為非圖形編輯策略。在後兩類(ConnectionEditPolicy 和 NodeEditPolicy)只覆寫 createDeleteCommand(),用於創建負責「刪除」操作的 Command 並返回,搞清此方法名中 create 和 delete 是對不同對象而言。

EditPart 實現

每個模型對象對應一個 EditPart,三個模型對象(Element 不算)分別對應 DiagramPart、ConnectionPart、NodePart。含子元素的 EditPart 必須覆寫 getModelChildren() 返回子對象列表,如 DiagramPart 返回 Diagram 包含的 Node 列表。

每個 EditPart 有 activate() 和 deactivate(),一般在前者註冊監聽器(因實現 PropertyChangeListener,EditPart 本身是監聽器)到模型,後者移除監聽器。觸發監聽器事件的 propertyChange() 中,根據「事件名」決定刷新視圖方式,如 NodePart:若節點本身屬性變化,調用 refreshVisuals();若相關連接變化,調用 refreshTargetConnections() 或 refreshSourceConnections()。事件名稱由我們自己規定,例中 Node.PROP_NAME 表示節點名稱屬性,Node.PROP_LOCATION 表示位置屬性等。

EditPart(確切說 AbstractGraphicalEditPart)另一重要方法是 createFigure(),返回模型在視圖中的圖形表示(IFigure 類型)。一般把圖形放在 figures 包,例中只有 NodeFigure 一個自定義圖形,Diagram 對應 GEF 自帶 FreeformLayer(可四方向任意擴展的層圖形);Connection 對應 PolylineConnection(缺省連接兩個圖形的直線),例中通過 setTargetDecoration() 讓連接目標端顯示箭頭。

最後,為 EditPart 增加適當 EditPolicy,覆寫 createEditPolicies() 實現。每個安裝到 EditPart 的 EditPolicy 對應一個角色字符串。對於模型中有子元素的 EditPart,一般安裝 EditPolicy.LAYOUT_ROLE 角色的 EditPolicy(多為 LayoutEditPolicy 子類);連接類型 EditPart 一般安裝 EditPolicy.CONNECTION_ENDPOINTS_ROLE 角色的 EditPolicy(多為 ConnectionEndpointEditPolicy 子類)等。

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

用戶操作被當前工具(缺省選擇工具 SelectionTool)轉換為請求(Request),請求按類型分發到目標 EditPart 安裝的 EditPolicy,後者根據請求對應角色判斷是否創建命令並執行。

前文說過,Role-EditPolicy-Command 設計主要為盡量重用代碼,如同一 EditPolicy 可安裝在不同 EditPart,同一 Command 可被不同 EditPolicy 使用等。但也有缺點:代碼不夠直觀,必須對眾多 Role、EditPolicy 了解,增加學習周期;大部分不需重用代碼也要按此相對複雜方式寫,帶來額外工作量。

以上是 GEF 應用最基本組成部分,例中還有 Direct Edit、屬性表、大綱視圖等功能未講解,後續將介紹。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved