weidagang2046的專欄

          物格而后知致
          隨筆 - 8, 文章 - 409, 評(píng)論 - 101, 引用 - 0
          數(shù)據(jù)加載中……

          使用圖形編輯框架創(chuàng)建基于 Eclipse 的應(yīng)用程序

          本文為您從頭到尾地介紹了使用 GEF 的步驟。我們不是完整地完成每個(gè)步驟,而是將使用您的應(yīng)用程序模型的子集,并先使該子集工作。例如,開始我們可能會(huì)忽略連接,或者只注重于您應(yīng)用程序中圖形元素類型的子集。

          GEF 概述

          GEF 假定您擁有一個(gè)希望以圖形方式顯示和編輯的模型。為了做到這一點(diǎn),GEF 提供了可在 Eclipse 工作臺(tái)中任何地方使用的查看器(類型為 EditPartViewer )。象 JFace 查看器一樣,GEF 查看器是 SWT 控件上的適配器。但是它們的類似之處僅此而已。GEF 查看器基于模型-視圖-控制器(model-view-controller,MVC)體系結(jié)構(gòu)。

          控制器作為視圖和模型之間的橋梁(請(qǐng)參閱圖 1)。每個(gè)控制器(即本文所謂的 EditPart)負(fù)責(zé)將模型映射到它的視圖,也負(fù)責(zé)對(duì)模型進(jìn)行更改。EditPart 還觀察模型并更新視圖,以反映模型狀態(tài)中的變化。EditPart 是一種對(duì)象,用戶將與這種對(duì)象進(jìn)行交互。稍后將更詳細(xì)地介紹 EditPart。

          圖 1. 模型-視圖-控制器
          模型-視圖-控制器

          GEF 提供了兩種查看器類型:圖形的和基于樹的。每種查看器都主管一種不同類型的 視圖。圖形查看器使用了在 SWT 畫布(Canvas)上繪制的 圖形(figure)。圖形是在 Draw2D 插件中定義的,該插件是 GEF 的一部分。TreeViewer 將 SWT 樹和 TreeItem 用于其視圖。





          回頁(yè)首


          第 1 步. 選定自己的模型

          GEF 對(duì)于模型一無(wú)所知。任何模型類型都可工作,只要它符合下面描述的特性。

          模型中有什么?

          所有東西都在模型中。模型是唯一會(huì)被持久存儲(chǔ)和恢復(fù)的東西。您的應(yīng)用程序應(yīng)當(dāng)將所有重要數(shù)據(jù)都存儲(chǔ)在模型中。在編輯、撤銷和重做的過程中,模型是唯一保持不變的。隨著時(shí)間推移,將對(duì)圖形和 EditPart 進(jìn)行垃圾收集處理并重新創(chuàng)建。

          當(dāng)用戶與 EditPart 交互時(shí),EditPart 并不直接操作模型。而是創(chuàng)建一個(gè)封裝了更改的 命令(Command)。命令可用來(lái)驗(yàn)證用戶的交互,并且提供撤銷和重做支持。

          嚴(yán)格地說(shuō),命令概念上也是模型一部分。它們 本身并不是模型,而是一些方法,模型是由這些方法編輯的。命令用于執(zhí)行用戶的所有可撤銷的更改。理論上,命令應(yīng)當(dāng)只了解模型。它們應(yīng)當(dāng)避免引用 EditPart 或圖形。類似地,如果可能,命令應(yīng)當(dāng)避免調(diào)用用戶界面(例如彈出式對(duì)話框)。

          兩個(gè)模型的故事

          一個(gè)簡(jiǎn)單的 GEF 應(yīng)用程序就是用于繪制圖的編輯器。(這里 只意味著圖片,而不是類圖等)圖可以被建模成某些形狀。一個(gè)形狀可能具有位置、顏色等特性,并且可能是多個(gè)形狀構(gòu)成的一組結(jié)構(gòu)。這里沒有什么可驚訝的,并且前述需求也易于維護(hù)(請(qǐng)參閱圖 2)。

          圖 2. 一個(gè)簡(jiǎn)單的模型
          一個(gè)簡(jiǎn)單的模型

          另一種常見的 GEF 應(yīng)用程序是 UML 編輯器,例如類圖編輯器。圖中的一段重要信息就是 (x, y) 位置,類就出現(xiàn)在該位置上。根據(jù)前一節(jié)的介紹,您可能會(huì)以為模型必須將一個(gè) 描述成具有 xy特性。大多數(shù)開發(fā)人員都不希望由于無(wú)意義的屬性而“污染”其模型。在這類應(yīng)用程序中,術(shù)語(yǔ)“業(yè)務(wù)”模型可用于指代基本模型,重要語(yǔ)義的詳細(xì)信息存儲(chǔ)在基本模型中。而特定于圖的信息存儲(chǔ)在“視圖”模型(它指的是業(yè)務(wù)模型中某樣?xùn)|西的“視圖”;在一個(gè)圖中可多次查看某個(gè)對(duì)象)中。有時(shí)候這種劃分甚至?xí)从吃诠ぷ骺臻g中,其中不同的資源可能被分別用來(lái)持久存儲(chǔ)圖和業(yè)務(wù)模型。甚至可能有多個(gè)圖對(duì)應(yīng)于同一個(gè)業(yè)務(wù)模型(請(qǐng)參閱圖 3)。

          圖 3. 劃分成業(yè)務(wù)模型和視圖模型的模型
          劃分成業(yè)務(wù)模型和視圖模型的模型

          不管您的模型劃分成了兩個(gè)部分,還是劃分成了多個(gè)資源,對(duì)于 GEF 而言這都是無(wú)關(guān)緊要的。術(shù)語(yǔ)模型用于指代整個(gè)應(yīng)用程序模型。屏幕上的一個(gè)對(duì)象可能對(duì)應(yīng)于模型中的多個(gè)對(duì)象。GEF 旨在允許開發(fā)人員方便地處理這類映射。

          通知策略

          對(duì)視圖進(jìn)行更新幾乎總是由來(lái)自模型的通知而導(dǎo)致的。您的模型必須提供某種通知機(jī)制,該機(jī)制必須映射到您應(yīng)用程序中相應(yīng)的更新。而只讀模型或不能進(jìn)行通知的模型(例如文件系統(tǒng)或遠(yuǎn)程連接)可能是例外。

          通知策略通常是分布式的(每對(duì)象)或集中式的(每域)。域通知器了解到對(duì)模型中任何對(duì)象的每次更改,然后將這些更改向域偵聽器廣播。如果您的應(yīng)用程序使用了這種通知模型,您可能要為每個(gè)查看器添加一個(gè)域偵聽器。當(dāng)該偵聽器接收到更改時(shí),它將查找受影響的 EditPart,然后適當(dāng)?shù)刂匦路峙稍摳摹H绻膽?yīng)用程序使用了分布式通知,那么每個(gè) EditPart 通常都將把自己的偵聽器添加到任何一個(gè)影響它的模型對(duì)象。





          回頁(yè)首


          第 2 步. 定義視圖

          下一步是決定將如何使用來(lái)自 Draw2D 插件的圖形顯示您的模型。某些圖形可直接用來(lái)顯示模型的某個(gè)對(duì)象。例如,Label 圖形可用來(lái)顯示 Image 和 String。有時(shí)候,通過組合多個(gè)圖形、布局管理器和/或邊框可以獲得期望的結(jié)果。最后,您可能要編寫自己的圖形實(shí)現(xiàn),該實(shí)現(xiàn)以特定于您應(yīng)用程序的方式繪圖。

          有關(guān)組合或?qū)崿F(xiàn)圖形和布局的更多信息可在 Draw2D 開發(fā)人員指南中找到,該指南包含在 GEF SDK 中。

          在和 GEF 一起使用 Draw2D 時(shí),通過遵循下列方針,可以使您的項(xiàng)目更便于管理,并且可以更靈活地更改需求:

          • 不要從頭做起。您可以組合所提供的布局管理器以呈現(xiàn)大多數(shù)東西。請(qǐng)考慮使用工具欄布局(在垂直或水平方向上)和邊框布局的組合來(lái)組合多個(gè)圖形。只有在萬(wàn)不得已時(shí)才編寫自己的布局管理器。作為參考,請(qǐng)查看 GEF 中提供的調(diào)色板。該調(diào)色板是使用 Draw2D 中的許多標(biāo)準(zhǔn)圖形和布局呈現(xiàn)的。

          • 保持 EditPart 和圖形之間“徹底”的分離。如果您的 EditPart 使用了幾個(gè)圖形、布局和/或邊框的復(fù)合結(jié)構(gòu),那么請(qǐng)盡量對(duì) EditPart 隱藏詳細(xì)信息。讓 EditPart 自己構(gòu)建所有東西是可能的(但不是個(gè)好主意)。不過這樣做并不會(huì)導(dǎo)致控制器和視圖之間“徹底”分離。EditPart 非常熟悉圖形結(jié)構(gòu),因此以類似的 EditPart 重用該結(jié)構(gòu)是不可能的。此外,更改外觀或結(jié)構(gòu)可能會(huì)導(dǎo)致意想不到的錯(cuò)誤。

            替代方法是,您應(yīng)當(dāng)編寫自己的 Figure 子類,該子類掩藏了圖形結(jié)構(gòu)的詳細(xì)信息。然后定義這個(gè)子類最少數(shù)量的 API,EditPart(控制器)用這些 API 來(lái)更新視圖。這種實(shí)踐(稱為 關(guān)注分離(separation of concerns))可提高重用機(jī)會(huì)并使錯(cuò)誤更少。

          • 不要從圖形引用模型或 EditPart。圖形不應(yīng)該具有對(duì) EditPart 或模型的訪問權(quán)。在某些情形中,EditPart 可能會(huì)將自己作為偵聽器添加到圖形,但是只會(huì)認(rèn)為它是偵聽器,而不是 EditPart。這種去耦合(de-coupling)實(shí)踐也可以產(chǎn)生更多的重用機(jī)會(huì)。

          • 使用內(nèi)容窗格。有時(shí)候您擁有一個(gè)容器,該容器將包含其它圖形元素,但是您需要在容器四周進(jìn)行一些裝飾。例如,一個(gè) UML 類通常顯示為框,其頂部標(biāo)有類名,可能還有一些原型,而底部是為屬性和方法保留的。通過組合多個(gè)圖形可以做到這一點(diǎn)。第一個(gè)圖形是類的標(biāo)題框,而另一個(gè)圖形被指派為 內(nèi)容窗格。該圖形將最終包含表示屬性和方法的圖形。稍后在編寫 EditPart 實(shí)現(xiàn)時(shí),并不一定要表明應(yīng)該將內(nèi)容窗格用作所有子元素的父元素。




          回頁(yè)首


          第 3 步. 編寫您的 EditPart - 控制器

          接下來(lái)我們將利用控制器(即 EditPart)將模型和視圖聯(lián)接起來(lái)。該步驟在 GEF 中放置“框架”。所提供的類是抽象的,因此客戶實(shí)際上必須編寫代碼。結(jié)果證明,生成子類不僅是人們熟悉的方法,而且可能也是將模型映射到視圖最靈活和簡(jiǎn)單的方法。

          所提供的用于生成子類的基本實(shí)現(xiàn)有三種。對(duì)于出現(xiàn)在樹查看器中的 EditPart 使用 AbstractTreeEditPart 。圖形查看器中的繼承 AbstractGraphicalEditPartAbstractConnectionEditPart 。本文將著重討論圖形 EditPart。相同的原理同樣適用于樹查看器。

          EditPart 生命周期

          在編寫您的 EditPart 之前,了解它們來(lái)自哪里,以及當(dāng)不再需要它們時(shí)怎樣處理它們,這是有幫助的。每個(gè)查看器都配置有一個(gè)用于創(chuàng)建 EditPart 的工廠(factory)。當(dāng)您設(shè)置查看器的內(nèi)容時(shí),通過提供表示該查看器輸入的模型對(duì)象,可以做到這一點(diǎn)。輸入通常是最頂部的模型對(duì)象,通過該對(duì)象可遍歷其它所有對(duì)象。然后查看器可以使用自己的工廠來(lái)構(gòu)造用于該輸入對(duì)象的 內(nèi)容 EditPart。之后,查看器中的每個(gè) EditPart 將填充和管理其自己的子 EditPart(和連接 EditPart),當(dāng)需要新的 EditPart 時(shí),將委派給 EditPart 工廠,直到填充該查看器。當(dāng)用戶添加新的模型對(duì)象時(shí),包含這些對(duì)象的 EditPart 將通過構(gòu)造相應(yīng)的 EditPart 做出響應(yīng)。請(qǐng)注意,視圖的構(gòu)造與 EditPart 的構(gòu)造是同時(shí)進(jìn)行的。因此,構(gòu)造每個(gè) EditPart 并將它添加到它的父 EditPart 之后,視圖(不管是圖形還是樹項(xiàng))也會(huì)發(fā)生同樣的過程。

          一旦用戶除去與某些 EditPart 對(duì)應(yīng)的模型對(duì)象,就丟棄這些 EditPart。如果用戶撤銷了一個(gè)刪除操作,那么用于表示被恢復(fù)對(duì)象而重新創(chuàng)建的 EditPart 與原先的 EditPart 是不同的。這就是為什么 EditPart 不能包含長(zhǎng)期信息,以及為什么不應(yīng)由命令引用的原因。

          您的第一個(gè) EditPart:內(nèi)容 EditPart

          您編寫的第一個(gè) EditPart 是對(duì)應(yīng)于圖本身的 EditPart。這個(gè) EditPart 稱為查看器的 內(nèi)容。它對(duì)應(yīng)于模型中最頂部的元素,并且其父元素為查看器的 EditPart(請(qǐng)參閱圖 4)。根通過提供各種圖形層(例如連接層和句柄層等)以及可能會(huì)在查看器級(jí)別提供的視圖縮放或其它功能,為內(nèi)容打下基礎(chǔ)。請(qǐng)注意,根的功能不依賴于任何模型對(duì)象,GEF 為根提供了幾個(gè)現(xiàn)成的實(shí)現(xiàn)。

          圖 4. 查看器中的 EditPart
          查看器中的 EditPart

          內(nèi)容的圖形不是很有趣,并且它通常只是一個(gè)空面板,該面板將包含圖的子圖。它的圖形應(yīng)該為不透明類型(opaque),并且應(yīng)當(dāng)利用布局管理器進(jìn)行初始化,該布局管理器將對(duì)圖的子圖進(jìn)行布局。但是,它將擁有結(jié)構(gòu)。圖的直系子圖是由返回的子模型對(duì)象列表確定的。清單 1 顯示了一個(gè)樣本內(nèi)容 EditPart,它創(chuàng)建了一個(gè)不透明類型的圖形,后者將使用 XYLayout 定位其子圖。


          清單 1. 內(nèi)容 EditPart 的初始實(shí)現(xiàn)
          												
          														public class DiagramContentsEditPart extends AbstractGraphicalEditPart {
              protected IFigure createFigure() {
                  Figure f = new Figure();
                  f.setOpaque(true);
                  f.setLayoutManager(new XYLayout());
                  return f;
              }
          
              protected void createEditPolicies() {
                  ...
              }
          
              protected List getModelChildren() {
                  return ((MyModelType)getModel()).getDiagramChildren();
              }
          }
          
          												
          										

          要確定圖上的項(xiàng),需要實(shí)現(xiàn)方法 getModelChildren() 。該方法返回子模型對(duì)象的列表(例如圖中的節(jié)點(diǎn))。超類將使用這個(gè)模型對(duì)象列表來(lái)創(chuàng)建對(duì)應(yīng)的 EditPart。新創(chuàng)建的 EditPart 被添加到部件的子 EditPart 列表。這樣又會(huì)將每個(gè)子 EditPart 的圖形添加到示例圖。缺省情況下,將返回一個(gè)空的列表,這表明沒有子對(duì)象。

          其它圖形 EditPart

          其余的 EditPart(表示圖中的項(xiàng))可能擁有要以圖形方式顯示的數(shù)據(jù)。它們可能還擁有自己的結(jié)構(gòu),例如連接或自己的子 EditPart。許多 GEF 應(yīng)用程序利用由標(biāo)簽注明的圖標(biāo)之間的連接來(lái)描述這些圖標(biāo)。讓我們假定您的 EditPart 將使用 Label 作為它的圖形,并且您的模型提供了名稱、圖標(biāo)以及與標(biāo)簽之間的連接。清單 2 顯示了實(shí)現(xiàn)這種類型 EditPart 的第一次嘗試。


          清單 2. “節(jié)點(diǎn)”EditPart 的初始實(shí)現(xiàn)
          												
          														public class MyNodeEditPart extends AbstractGraphicalEditPart {
              protected IFigure createFigure() {
                  return new Label();
              }
              protected void createEditPolicies() {
                  ...
              }
              protected List getModelSourceConnections() {
                  MyModel node = (MyModel)getModel();
                  return node.getOutgoingConnections();
              }
              protected List getModelTargetConnections() {
                  MyModel node = (MyModel)getModel();
                  return node.getIncomingConnections();
              }
              protected void refreshVisuals() {
                  MyModel node = (MyModel)getModel();
                  Label label = (Label)getFigure();
                  label.setText(node.getName());
                  label.setIcon(node.getIcon());
          
                  Rectangle r = new Rectangle(node.x, node.y, -1, -1);
                  ((GraphicalEditPart) getParent()).setLayoutConstraint(this, label, r);
              }
          }
          
          												
          										

          這里覆蓋了一個(gè)新方法 refreshVisuals() 。當(dāng)需要利用來(lái)自模型的數(shù)據(jù)更新圖形時(shí),就調(diào)用該方法。在本案例中,模型的名稱和圖標(biāo)被反映在標(biāo)簽中。但更為重要的是,該標(biāo)簽是通過將其布局約束傳遞給父元素來(lái)定位的。在內(nèi)容 EditPart 中,我們使用了 XY 布局管理器。該布局使用 Rectangle 約束來(lái)確定在何處放置子圖形。寬和高的值為“-1”,表明應(yīng)當(dāng)為該圖提供理想的大小。

          技巧 #1

          決不要使用 setBounds(...) 方法來(lái)放置圖形。使用諸如 XYLayout 之類的布局管理器確保會(huì)正確地更新滾動(dòng)條。另外,XYLayout 還處理將相對(duì)約束轉(zhuǎn)換成絕對(duì)位置的工作,并且可用它來(lái)將圖形自動(dòng)調(diào)整為理想的大小(換而言之,如果約束的寬和高為 -1)。

          方法 refreshVisuals() 僅在 EditPart 初始化的過程中調(diào)用一次,并且決不會(huì)被再次調(diào)用。在響應(yīng)模型通知時(shí),應(yīng)用程序負(fù)責(zé)根據(jù)需要再次調(diào)用 refreshVisuals() 以更新圖形。要改進(jìn)性能,您可能希望將每個(gè)模型屬性的代碼分解成其自己的方法(或者是一個(gè)帶有“開關(guān)(switch)”的方法)。這樣,當(dāng)模型發(fā)出通知時(shí),可以運(yùn)行最少的代碼以便只刷新那些發(fā)生更改的內(nèi)容。

          另一個(gè)有趣的區(qū)別是連接支持的代碼。與 getModelChildren() 類似, getModelSourceConnections()getModelTargetConnections() 應(yīng)當(dāng)返回表示節(jié)點(diǎn)之間連接的模型對(duì)象。超類在必要時(shí)創(chuàng)建對(duì)應(yīng)的 EditPart,并將它們添加到源和目標(biāo)連接 EditPart 列表。請(qǐng)注意,連接是由每端的節(jié)點(diǎn)引用的,而其 EditPart 只需創(chuàng)建一次。GEF 確保只創(chuàng)建一次連接,該工作是通過首先檢查查看器中是否已經(jīng)存在連接來(lái)完成的。





          回頁(yè)首


          建立連接

          編寫連接 EditPart 實(shí)現(xiàn)沒有太大的區(qū)別。首先生成 AbstractConnectionEditPart 的子類。跟前面一樣,可以實(shí)現(xiàn) refreshVisuals() ,以將屬性從模型映射到圖形。連接可能還擁有約束,盡管這些約束與前面的約束略有不同。這里,連接路由器使用約束來(lái)使連接轉(zhuǎn)向(bend)。此外,連接 EditPart 的圖形必須是 Draw2D Connection ,它引入了另一個(gè)需求:連接錨(connection anchor)。

          連接必須由 ConnectionAnchor “錨定”在兩端。因此,必須在連接 EditPart 中或在節(jié)點(diǎn)實(shí)現(xiàn)中表明使用哪些錨。缺省情況下,GEF 假定節(jié)點(diǎn) EditPart 將通過實(shí)現(xiàn) NodeEditPart 接口而提供錨。這樣假定的一個(gè)原因是,錨的選擇取決于各端上的節(jié)點(diǎn)正在使用的圖形。連接 EditPart 不應(yīng)了解節(jié)點(diǎn)正在使用的圖形的任何內(nèi)容。另一個(gè)原因是,當(dāng)用戶創(chuàng)建連接時(shí),連接 EditPart 是不存在的,因此節(jié)點(diǎn)必須能夠自己顯示反饋。作為清單 2 的延續(xù),我們?cè)谇鍐?3 中添加了必要的錨支持。


          清單 3. 將錨支持添加到“節(jié)點(diǎn)”EditPart
          												
          														public class MyNodeEditPart
              extends AbstractGraphicalEditPart
              implements NodeEditPart
          {
              ...
              public ConnectionAnchor getSourceConnectionAnchor(ConnectionEditPart connection) {
                  return new ChopboxAnchor(getFigure());
              }
              public ConnectionAnchor getSourceConnectionAnchor(Request request) {
                  return new ChopboxAnchor(getFigure());
              }
              public ConnectionAnchor getTargetConnectionAnchor(ConnectionEditPart connection) {
                  return new ChopboxAnchor(getFigure());
              }
              public ConnectionAnchor getTargetConnectionAnchor(Request request) {
                  return new ChopboxAnchor(getFigure());
              }
          
              ...
          }
          
          												
          										

          技巧 #2

          別忘記真正實(shí)現(xiàn) NodeEditPart 接口。否則您的方法將永遠(yuǎn)不會(huì)被調(diào)用。

          以連接(connection)為參數(shù)的方法是對(duì)現(xiàn)有連接 EditPart 設(shè)置錨時(shí)使用的方法。其它兩個(gè)方法以請(qǐng)求(request)作為參數(shù)。這些方法是用戶創(chuàng)建新連接時(shí)的編輯過程中使用的。對(duì)于本示例,所有情形都將返回 chopbox 錨。chopbox 錨只查找線與節(jié)點(diǎn)圖形的邊框相交的點(diǎn)。

          實(shí)現(xiàn)連接 EditPart 是比較簡(jiǎn)單的。請(qǐng)注意,甚至無(wú)需創(chuàng)建圖形,因?yàn)槿笔〉?PolylineConnection 創(chuàng)建適合于大多數(shù)場(chǎng)合(請(qǐng)參閱清單 4)。


          清單 4. 初始的連接 EditPart 實(shí)現(xiàn)
          												
          														public class MyConnectionEditPart extends AbstractConnectionEditPart {
              protected void createEditPolicies() {
                  ...
              }
          
              protected void refreshVisuals() {
                  PolylineConnection figure = (PolylineConnection)getFigure();
                  MyConnection connx = (MyConnection)getModel();
                  figure.setForegroundColor(MagicHelper.getConnectionColor(connx));
                  figure.setRoutingConstraint(MagicHelper.getConnectionBendpoints(connx));
              }
          }
          
          												
          										

          技巧 #3

          最重要的是了解何時(shí)使用以及何時(shí)不使用 ConnectionEditPart。當(dāng)用戶可以選擇某些東西并可與之進(jìn)行交互的時(shí)候,就使用連接 EditPart。它可能與模型中的某個(gè)對(duì)象直接相關(guān),并且通常可由自己刪除。

          如果您只是擁有一個(gè)需要繪制直線的節(jié)點(diǎn)或容器,那么只須用圖形的 paint 方法繪制直線,或者組合一個(gè)圖形,該圖形包含 Polyline 圖形。

          連接始終都必須擁有源和目標(biāo)。如果您需要一個(gè)連接,該連接可以在沒有源或沒有目標(biāo)的情況下存在,那么比較好的方法是只繼承 AbstractGraphicalEditPart ,并使用連接圖形。

          偵聽模型

          創(chuàng)建 EditPart 之后,它應(yīng)該開始偵聽來(lái)自模型的更改通知。由于 GEF 是與模型無(wú)關(guān)的,因此所有應(yīng)用程序都必須添加自己的偵聽器,并處理產(chǎn)生的通知。接收到通知時(shí),處理程序可以調(diào)用某個(gè)提供的方法來(lái)強(qiáng)制進(jìn)行一次刷新。例如,如果刪除了一個(gè)子模型對(duì)象,那么調(diào)用 refreshChildren() 將導(dǎo)致對(duì)應(yīng)的 EditPart 及其圖形被除去。對(duì)于簡(jiǎn)單的屬性更改,可以使用 refreshVisuals() 。正如我們前面提及的,可將該方法分解成幾個(gè)部分,從而避免沒有必要地更新每個(gè)顯示的屬性。

          添加偵聽器但卻忘記除去它們是導(dǎo)致內(nèi)存泄漏的常見原因。出于這個(gè)原因,添加和除去偵聽器的地方應(yīng)該在 API 中清晰地注明。您的 EditPart 必須繼承 activate() ,以便添加稍后必須除去的任何偵聽器。通過繼承 deactivate() 可除去那些相同的偵聽器。清單 5 顯示了向節(jié)點(diǎn) EditPart 實(shí)現(xiàn)添加的模型通知內(nèi)容。


          清單 5. 偵聽“節(jié)點(diǎn)”EditPart 中的模型更改
          												
          														public class MyNodeEditPart
          extends AbstractGraphicalEditPart
              implements NodeEditPart, ModelListener
          {
              ...
          
              public void activate() {
                  super.activate();
                  ((MyModel)getModel()).addModelListener(this);
              }
          
              public void deactivate() {
                  ((MyModel)getModel()).removeModelListener(this);
                  super.deactivate();
              }
          
              public void modelChanged(ModelEvent event) {
                  if (event.getChange().equals("outgoingConnections"))
                      refreshSourceConnections();
                  else if (event.getChange().equals("incomingConnections"))
                      refreshTargetConnections();
                  else if (event.getChange().equals("icon")
                    || event.getChange().equals("name"))
                      refreshVisuals();
              }
          
              ...
          }
          
          												
          										

          編輯模型

          到目前為止,我們已經(jīng)講解了如何創(chuàng)建 EditPart,它們?nèi)绾蝿?chuàng)建自己的可視圖(visual)以及當(dāng)模型發(fā)生變化時(shí)它們?nèi)绾巫晕腋隆3酥猓珽ditPart 也是對(duì)模型進(jìn)行更改的主要參與者。當(dāng)命令的請(qǐng)求被發(fā)送給 EditPart 時(shí)就會(huì)發(fā)生這種情況。請(qǐng)求還用來(lái)要求 EditPart 顯示諸如在鼠標(biāo)拖動(dòng)期間發(fā)生的反饋。EditPart 支持、阻止或忽略給定的請(qǐng)求。所支持或阻止的請(qǐng)求類型決定了 EditPart 的行為。

          到目前為止,側(cè)重點(diǎn)都是將模型的結(jié)構(gòu)和特性映射到視圖。結(jié)果表明,這基本上是您在 EditPart 類自身中所做的所有工作。其行為是由一組名為 EditPolicies 的可插入的助手類決定的。在所提供的示例中,我們忽略了方法 createEditPolicies() 。一旦您實(shí)現(xiàn)該方法,您就幾乎已經(jīng)完成了您的 EditPart。當(dāng)然,您仍需要提供編輯策略,該策略知道如何修改您應(yīng)用程序的模型。

          因?yàn)榫庉嬓袨槭强刹迦氲模栽陂_發(fā)各種 EditPart 實(shí)現(xiàn)時(shí),可以根據(jù)將模型映射到視圖和處理模型更新這種任務(wù),來(lái)創(chuàng)建類層次結(jié)構(gòu)。





          回頁(yè)首


          第 4 步. 將所有內(nèi)容組合在一起

          現(xiàn)在,您已經(jīng)完成了以圖形方式顯示您的模型所需的所有部分。對(duì)于最后的組裝,我們將使用 IEditorPart 。但是,也可以在視圖、對(duì)話框或者可以放置控件的幾乎任何地方使用 GEF 的查看器。對(duì)于本步驟,您必須擁有自己的 UI 插件,它將為正在打開的資源定義編輯器和文件擴(kuò)展名。可在同一個(gè)插件或一個(gè)不同的插件中定義您的模型。您還需要一個(gè)預(yù)填充的模型,因?yàn)槟壳斑€沒有編輯功能。

          提供樣本模型數(shù)據(jù)的方法有幾種。對(duì)于本示例而言,當(dāng)編輯器打開時(shí),我們將在代碼中創(chuàng)建模型,從而忽略了文件的實(shí)際內(nèi)容。要做到這一點(diǎn),我們假定已經(jīng)存在一個(gè)測(cè)試工廠。或者,您可以創(chuàng)建一個(gè)示例向?qū)В撓驅(qū)в脭?shù)據(jù)對(duì)資源進(jìn)行預(yù)填充(普通向?qū)е粍?chuàng)建空?qǐng)D)。最后,您可以利用文本編輯器以手工方式編寫文檔的內(nèi)容。

          既然您有了樣本模型,那么讓我們創(chuàng)建將顯示模型的編輯器部件。有一種快速的方法,就是生成子類或復(fù)制 GEF 的 GraphicalEditor 。該類創(chuàng)建 ScrollingGraphicalViewer 的一個(gè)實(shí)例,并且構(gòu)造一個(gè)畫布來(lái)充當(dāng)編輯器的控件。它是一個(gè)很方便的類,用來(lái)幫助您開始使用 GEF;一個(gè)可以正確工作的 Eclipse 編輯器需要考慮很多其它事情,例如不利的團(tuán)隊(duì)環(huán)境(pessimistic team environment)和要?jiǎng)h除或移動(dòng)資源等。

          清單 6 顯示了一個(gè)樣本編輯器實(shí)現(xiàn)。有幾個(gè)必須實(shí)現(xiàn)的抽象方法。出于本文所討論范圍的限制,我們將忽略模型持久性和標(biāo)記。要讓您的圖出現(xiàn)在圖形查看器中,您必須做兩件事情。首先,利用自己的 EditPart 工廠配置查看器,以從第 3 步構(gòu)造 EditPart。然后,將圖模型對(duì)象傳遞給查看器。


          清單 6. 實(shí)現(xiàn)您的編輯器部件(Editor Part)
          												
          														public class MyEditor extends GraphicalEditor {
              public MyEditor() {
                  setEditDomain(new DefaultEditDomain(this));
              }
          
              protected void configureGraphicalViewer() {
                  super.configureGraphicalViewer(); //Sets the viewer's background to System "white"
                  getGraphicalViewer().setEditPartFactory(new MyGraphicalEditpartFactory());
              }
          
              protected void initializeGraphicalViewer() {
                  getGraphicalViewer().setContents(MagicHelper.constructSampleDiagram());
              }
              public void doSave(IProgressMonitor monitor) {
                  ...
              }
              public void doSaveAs() {
                  ...
              }
              public void gotoMarker(IMarker marker) {
                  ...
              }
              public boolean isDirty() {
                  ...
              }
              public boolean isSaveAsAllowed() {
                  ...
              }
          }
          
          												
          										





          回頁(yè)首


          接下來(lái)的步驟

          我們已經(jīng)經(jīng)歷了從僅擁有一個(gè)模型到在圖形編輯器中顯示該模型的全過程。但是我們只打下了基礎(chǔ)。我們簡(jiǎn)要提及了編輯策略。通過閱讀與 GEF SDK 一起提供的開發(fā)人員文檔,可以獲得有關(guān)編輯策略的更多信息。還可從 GEF 的主頁(yè)(請(qǐng)參閱文章結(jié)尾的 參考資料)獲得一個(gè)示例,該示例演示了如何使用各種編輯策略類型。

          GEF 還提供了一個(gè)調(diào)色板。該調(diào)色板顯示了一組工具,用于在圖中創(chuàng)建對(duì)象。用戶可以激活這些工具,或者使用本機(jī)拖放直接從該調(diào)色板拖動(dòng)項(xiàng)。它還支持讓用戶定制內(nèi)容。

          在 GEF 中還可以使用幾個(gè) JFace 操作。應(yīng)用程序可以在菜單、工具欄或上下文菜單中使用諸如撤銷、對(duì)齊和刪除之類的操作。

          最后,您的應(yīng)用程序應(yīng)當(dāng)支持大綱(outline)視圖和特性(properties)視圖。大綱視圖用于導(dǎo)航和有限的編輯用途。GEF 的 TreeViewer 和/或概述(overview)窗口可以在這里使用。特性表(property sheet)允許用戶查看和編輯任何當(dāng)前選定項(xiàng)的詳細(xì)特性。

          為了顯示所選擇的項(xiàng)并允許用戶進(jìn)行更改,您必須將編輯策略添加到 EditPart。請(qǐng)參閱 GEF 主頁(yè)上的示例,并參閱與 GEF SDK 一起提供的開發(fā)人員文檔。

          有關(guān) GEF 和 Eclipse 工作臺(tái)提供的其它功能的詳細(xì)信息不在本文的討論范疇之內(nèi),但是您可能有興趣對(duì)它們稍做了解:

          • 調(diào)色板。該工具的調(diào)色板是用于在圖中創(chuàng)建新對(duì)象的 實(shí)際方法。GEF 包含了一個(gè)功能豐富的調(diào)色板,它支持拖放、多繪圖程序和布局設(shè)置,如果應(yīng)用程序希望,它甚至可支持讓用戶定制內(nèi)容。
          • 操作欄。編輯器(Editor)和視圖(View)可以為工具欄、菜單和上下文菜單提供操作(Action)。GEF 提供了幾個(gè)可重用的操作實(shí)現(xiàn),但是將它們顯示在什么地方取決于應(yīng)用程序。
          • 特性表。特性表可用于顯示所選項(xiàng)特性的詳細(xì)信息。GEF 允許您在 EditPart 上或在模型中添加特性表支持。
          • 大綱。大綱視圖通常用于顯示圖的結(jié)構(gòu)化表示,但是一般來(lái)說(shuō),它可用于任何工作。GEF 的 TreeViewer 通常在大綱視圖中使用。




          回頁(yè)首


          參考資料





          回頁(yè)首


          關(guān)于作者

          Randy Hudson 是北卡羅來(lái)納州 Research Triangle Park 的 IBM 軟件工程師。作為圖形編輯框架(Graphical Editing Framework,GEF)的技術(shù)領(lǐng)導(dǎo),他幫助將這個(gè)曾經(jīng)是內(nèi)部的項(xiàng)目轉(zhuǎn)變成了開放源碼技術(shù)。他目前的工作側(cè)重于可用性、圖形編輯、圖形布局和邊緣路由(edge routing)。可以通過 buchu at nc.rr.com與 Randy 聯(lián)系。


          from: http://www-128.ibm.com/developerworks/cn/linux/opensource/os-gef/index.html

          posted on 2006-09-04 10:57 weidagang2046 閱讀(318) 評(píng)論(0)  編輯  收藏 所屬分類: Java

          主站蜘蛛池模板: 澜沧| 健康| 得荣县| 大理市| 施秉县| 富蕴县| 崇左市| 天祝| 丹凤县| 衡阳市| 西丰县| 潼南县| 崇文区| 平潭县| 虞城县| 定西市| 吉木萨尔县| 鸡泽县| 象山县| 德兴市| 全南县| 金坛市| 禹城市| 五莲县| 江陵县| 上思县| 万年县| 大宁县| 台中县| 于田县| 札达县| 会理县| 武义县| 车致| 商丘市| 荃湾区| 长丰县| 巩义市| 南澳县| 徐水县| 林口县|