uo 最近在學習GEF,本例子主要是修改了八進制的例子。在這里做一下總結。先來介紹一下gef,網上關于GEF的介紹越來越多,從另一方面也說明這個技術確實在熱起來,不過GEF為了實現可擴放性和可重用性,做的很是復雜(閑話一下,最近看了一本書《Better.Faster.Lighter.Java》,沒看多少,Justin Gehtland, Bruce A. Tate寫的,一開始就說了,為了滿足越來越多的需要,各種framework,library,language都是越來越復雜了,學習曲線也變得陡峭了,這個似乎是無法避免的,java語言也是,不信去看看jdk,1.1時才3.7MB,1.4時已經38MB了。我也一直堅信,Simply the best,呵呵)。
GEF的一些資源:
1。www.eclipse.org 這個是源頭啊,faq也可以看看
2。ibm的dW 除了相關文章,這里還有本關于gef和emf的紅寶書,可以免費下載。
3。eclipseworld.org 這個是一個關于eclipse中文社區
4。八進制的blog http://bjzhanghao.cnblogs.com/ 。
5。這里還有些中文教程 http://www.benisoft.com/cn/index.htm
6。還有一些blog http://www.aygfsteel.com/hopeshared/ http://www.aygfsteel.com/eclipshine/ http://www.aygfsteel.com/reloadcn/ http://www.aygfsteel.com/USTCEric
好了,現在來介紹一下GEF的實現機制,主要是我的一些理解可能還有一不對的地方,歡迎大家幫我改正。
GEF是具有標準MVC(Model-View-Control)結構的圖形編輯框架,其中Model由我們自己根據業務來設計,它要能夠提供某種模型改變通知的機制,用來把Model的變化告訴Control層;Control層由一些EditPart實現,EditPart是整個GEF的核心部件;而 View層,大多數情況下,使用Draw2D實現了,其作用是把Model以圖形化的方式表現給使用者,當然你也可以使用其他圖形包,不過gef與 Draw2D的結合還是非常緊密的,一般為了使用方便,View都是使用Draw2D實現的。
關于Draw2D,八進制有一篇相應的文章,還有eclipse上的一篇文章,可以參考,這里有幾點需要強調一下:
1。Draw2D提供了很多缺省圖形,最常見的有三類:1、形狀(Shape),如矩形、三角形、橢圓形等等;2、控件(Widget),如標簽、按鈕、滾動條等等;3、層(Layer),它們用來為放置于其中的圖形提供縮放、滾動等功能。
2。每個圖形都可以擁有一個邊框(Border),Draw2D所提供的邊框類型有GroupBoxBorder、TitleBarBorder、ImageBorder、 ButtonBorder,以及可以組合兩種邊框的CompoundBorder等等,在Draw2D里還專門有一個Insets類用來表示邊框在圖形中所占的位置,它包含上下左右四個整型數值。
3。一個圖形可以包含很多個子圖形,這些被包含的圖形在顯示的時候必須以某種方式被排列起來,負責這個任務的就是父圖形的LayoutManager,相應的約束(Constraint)對象就在這里,比如在子圖形刷新(refreshVisuals)時可以通過將其布局約束傳遞給其父元素來定位。
4。利用Draw2D中的Router、Anchor和Locator,可以實現多種連接樣式。Router負責連接線的外觀和操作方式;Anchor控制連接線端點在圖形上的位置,即"錨點"的位置;Locator的作用是定位圖形。
前面說過了GEF是通過MVC架構實現的,下面分別介紹這三個方面:
一、模型(Model):這個是相對比較容易理解,模型保存著業務邏輯,他是GEF這中唯一可以持久化的,并且需要提供相應的監聽機制,來通知 EditPart去更新View,監聽機制又可以分為分布式和集中式兩種,我們可以針對域監聽器進行轉有通知,也可以全局廣播;在這里我們認為 Command(EditPart用來更新模型的對象)也是模型的一個部分,因為它了解模型。此外,在有時候可能需要多種模型。我們對于業務模型,提供一個“視圖”模型,這樣便于實現和滿足相應的要求,不管內部模型是怎么樣的,都要實現通知機制。
二、視圖。GEF提供了在EclipseWorkbench中使用Viewer,稱為EditPartViewer。它的作用和jface中的 Viewer很相,不過這時候它的ContentProvider和LabelProvider是EditPart,通過setContents設置(注意在八進制代碼中setContents參數為模型對象,通過分析發現,在AbstractEditPartViewer中此方法被重載,可以除了接受 org.eclipse.gef.editpart對象外可以接受Object對象,不過此時回調用EditPartFactory,通過給定的模型對象來創建相應的EditPart,個人認為這樣做并不與“View與Model無直接聯系”的要求相矛盾)。這個接口繼承了 org.eclipse.jface.viewers.ISelectionProvider接口(這個也和jface中的viewer一樣),因此使 viewer提供了監聽器,這樣可以將對view的操作傳遞到editpart。EditPartViewer會維護各個EditPart的選中狀態,如果沒有被選中的EditPart,則缺省選中的是作為contents的EditPart。
EditPartViewer可以適配到任何一個SWT控件,因為它要求有createControl方法,接受一個父SWT控件作為適配對象,從而將 GEF生成的Figure對象顯示在這個SWT控件上,因此GEF可以使用任何圖形包,不過GEF提供了對Draw2D的默認實現,比如 AbstractGraphicalEditPart類中的回調方法createFigure就要求返回一個IFigure對象。將一個GEF圖形顯示出來可以使用org.eclipse.ui.part.EditorPart也可以是org.eclipse.ui.part.ViewPart,如果使用 EditorPart,則需要實現繼承自WorkbenchPart的createPartControl,如果是ViewPart則需要自己實現與 GEF的交互,不過Eclipse Workbench對viewpart和editorpart的支持是一樣的,都是在plugin文件擴展相應的擴展點。一般來說,使用GEF對 Editorpart的模式實現是非常方便的。
GEF目前提供兩種視圖分別是GraphicalViewer和TreeViewer,都間接繼承了EditPartViewer接口,前者利用 Draw2D圖形(IFigure)作為表現方式,多用于編輯區域,后者則多用于實現大綱展示,并且他們有兩個具體的實現,分別是 ScrollingGraphicalViewer和TreeViewer。其中ScrollingGraphicalViewer,通過分析GEF源代碼可知,是GraphicalEditor中Viewer的默認實現,我們可以通過GraphicalEditor類提供的 setGraphicalViewer(GraphicalViewer)方法重新設置其他viewer。我們在使用的時候通過 configureGraphicalViewer和initializeGraphicalViewer方法來配置GraphicalViewer。所不同的是,在configureGraphicalViewer之后initializeGraphicalViewer之前插入了一個 hookGraphicalViewer方法,其作用是同步選擇和把 EditPartViewer作為SelectionProvider注冊到所在的site,使其成為SelectionProvider。
此外,GEF中還提供一種成為調色板(PaletteViewer)的視圖,它也是間接實現了EditPartViewer接口。因此,我也像配置GraphicalViewer一樣來配置PaletteViewer,類似的使用 configurePaletteViewer和initializePaletteViewer方法,其間類似的插入了一個 hookPaletteViewer方法,用于設置相應EditDomain的PaletteViewer。
最后,總結一下如何配置GraphicalView,主要是四個方面:
1。設置一個RootEditPart:RootEditPart的是使整個GEF框架運行起來的關鍵之一。RootEditPart并不對應于任何的模型對象,它將從setContents()方法中接收到的模型對象進行轉換,并添加到整個的EditPart體系中去。
2。設置其EditPartFactory:負責從模型到EditPart的轉換。一般來說一個模型對象對應于一個EditPart。
3。設置EditDomain:用來接收事件并選擇恰當的事件處理函數進行處理,這個主要是為了相應view所接收到的用戶的操作。
4。調用setContents()方法:為其設置要顯示的內容。
三、控制器(EditPart)。這是GEF最核心的地方了,它將model和view聯系起來。程序員可以繼承三種EditPart,分別是 org.eclipse.gef.editparts.AbstractGraphicalEditPart,org.eclipse.gef.editparts.AbstractConnectionEditPart(AbstractGraphicalEditPart的子類)和org.eclipse.gef.editparts.AbstractTreeEditPart。EditPart 的生命周期當setContents時,將模型對象輸入,此對象為頂層對象,通過它可以遍歷所有的模型對象。:EditPartViewer配備有 EditPartFactory,EditPartViewer通過EditPartFactory構造相應的Contents EditPart。然后EditPartViewer中的每個EditPart通過EditPartFactory構造相應的子EditPart。當用戶添加新的模型的時候,包含這些模型對象的所對應的EditPart將構造相應的EditPart作出相應。視圖的構造和EditPart的構造是同時進行的,每當EditPart構造并添加到它的父EditPart后相應的view也做同樣的事情,比如GraphicalEditor中就有 GraphicalViewer成員變量,在此edipart創建時回調用createPartControl,在這個方法中將會創建相應的view,并將其加入到父view中。
一旦用戶丟棄了某些模型對象,相應的EditPart就會被丟棄。比如用戶執行了刪除操作,則相應的模型對象和相應的EditPart都被刪除了,但用戶撤銷了刪除操作而重新創建的EditPart與原來的EditPart是不同的,所以EditPart是不能保存長期信息的,應該交給模型去保存,并且 EditPart也是不能被Command所引用。
在具體使用時,應該先定一個Contents EditPart,它的父元素為EditPartViewer的RootEditPart。RootEditPart是EditPartView的 root,它將EditPartView和它的contents(一般認為是頂層模型對象所對應的EditPart)聯系起來,RootEditPart 為用戶的EditPart提供一致性的上下文(context)。并且,當沒有被選中的模型EditPart時,頂層對象的EditPart是默認被選中的,而RootEditPart是不能被選中的或者定位的,甚至不能與用戶相交互,他也就沒有對應的模型對象。GEF提供了幾個默認的 RootEditPart的實現:FreeformGraphicalRootEditPart,RootTreeEditPart, ScalableRootEditPart,ScalableFreeformRootEditPart(此類繼承于 FreeformGraphicalRootEditPart,增加了縮放Zoom支持,提供了ZoomManager;增加了一個LayerPane: ScalableFreeformLayeredPane。),(原來還有個GraphicalRootEditPart,已經deprecated,用 ScalableRootEditPart代替)。這里要注意的是:
1)對于FreeformRootEditPart(以及它的子類 ScalableFreeformRootEditPart),它的Contents EditPart應該有一個org.eclipse.draw2d.FreeformFigure對象(比如FreeformLayer或FreeformLayeredPane)作為它的Figure。例如在我們的代碼里,對應的就是在 DiagramPart中的createFigure方法,它返回了一個FreeformLayer對象。
2) FreeformGraphicalRootEditPart的Primery Layer沒有使用draw2d的LayoutManager,并且,除非contents的figure是個freeform figure,否則不能正確的調整大小。
3)FreefromGraphicalRootEditPart使用FreeformViewport類作為它的primary figure,這個類必須和ScrollingGraphicalViewer使用。
4)ScalableRootEditPart使用 Viewport作為primery figure,這個類必須和ScrollingGraphicalViewer使用。
5)RootTreeEditPart是TreeViewer的RootEditPart。
Contents EditPart的圖形一般是一個空面板,該面板將包含diagram的子圖。
我們還要定義其他“視圖”模型的EditPart,比如節點(node),連接(connection)。對于節點,是Graphical,可以繼承 AbstractGraphicalEditPart。并且可以實現NodeEditPart接口,則可以支持源和目標的 ConnectionEditPart,使用Anchor,具體在下面有講述。在這里,我們的代碼的NodePart類中,重載了 refreshVisual方法,其中設置figure的布局約束是通過它的父元素來定位的,我不知道這是不是標準的方法,XYLayout使用的是 rectangle約束,如果長寬設為-1,表示為該圖形提供理想大小。不要使用setBounds,XYLayout布局管理器可以保證對滾動條進行更新,并能將相對約束轉化為絕對約束,如果長寬設置為-1,則能夠將圖形設置為理想的大小。此外,為了改進性能,我們可以將refreshVisual中有若干模型的分別刷新,或者設置是否需要刷新的開關。
對于連接,可以繼承AbstractConnectionEditPart,這個抽象類已經實現了 ConnectionEditPart。和Node實現一樣,通過refreshVisual方法將屬性從模型映射到圖形。連接也可以有約束,不過與前面的約束不盡相同。在這里,連接路由器(connection router)使用約束來是連接轉向(bend)。需要注意的是,一個Connection EditPart的圖形必須是一個draw2d的Connection,這樣就引入了連接錨(Connection Anchor)。連接必須由連接錨(ConnectionAnchor)“錨定”在兩端。因此,需要在連接EditPart或者節點EditPart中實現使用什么樣的錨。缺省情況下,GEF使用節點EditPart通過實現NodeEditPart接口來提供錨。這是因為,錨的選擇取決與各個節點上正在使用的圖形,而連接EditPart不應了解節點上的圖形,并且,當用戶創建連接的時候,連接EditPart并沒有創建,這時候就由節點自己顯示反饋。這里需要注意的是:何時使用以及何時不使用ConnectionEditPart,當用戶可以選擇某些東西并可與之交互時,就需要 ConnectionEditPart。它可能與某行中的某個對象相對應,并且回自己刪除;如果這是需要一個繪制直線的節點或者容器,那么只需要用圖形的 paint方法繪制直線,或者組合一個圖形,這個圖形包含Polyline圖形。連接必須要擁有源和目標。如果要創建一個連接,并且在沒有源和目標存在的情況下,可以繼承AbstractGraphicalEditPart,并使用連接圖形,比如PolylineConnection。
正如前面說的,模型需要實現某種通知機制,各個EditPart要將自己做為監聽者,注冊在模型上,以便在模型發生改變的時候收到相應的通知。 當EditPart收到相應的通知時,調用相應的處理方法,來進行一次強制的刷新,對于模型對象的增減可以調用refreshChildren方法,對于簡單屬性的更改可以調用refreshVisuals方法,這時,像前面提到的,可以只刷新改變的部分,以提高性能。一般,我們在Activate方法中注冊監聽器,在Deactivate方法中注銷監聽器。
以上完成的是從模型到視圖的映射,你還需要實現的是編輯策略Edit Policies,因為EditPart并不直接對模型進行編輯,每一個EditPolicy都針對一個特定的編輯任務或者一組相關的工作。這樣的實現便于在不同的EditPart間重用(reuse)編輯行為,并且這樣的實現,可以動態的改變行為,比如改變layout或者routing時。在 createEditPolicies方法中,我們要insall我們的的Policies,這時候就需要roles,roles的作用就是作為關鍵字來標示不同的policies。policy中提供Command,每個應用程序都有個Command棧,Command的運行要通過Command棧,而不是直接調用execute方法。
說到這里,要注意EditPartView是視圖的,一般放在一個UI插件上比如EditorPart,viewPart,甚至swt控件。EditPart是控制器,聯系視圖和模型。
在本文的最后介紹一下GEF的運行機制:EditPartView接受用戶的操作,如節點的移動,增刪,每個節點對應一個EditPart對象,每個 EditPart都有一組由Role區分的EditPolicies,每個EditPolicy會對應一些Command對象,Command最終會對模型進行操作。用戶的操作會轉化為Request分配給特定的EditPolicy,由EditPolicy創建相應的Command對模型進行操作,這些 Command會保留在EditDomain的命令堆棧中,用于實現undo,redo功能,EditDomain是專門用于維護 EditPartView,Command信息的對象,一般每個EditPart有一個EditDomain。
以上就是對所作的GEF部分的大概描述,可能有些地方寫的比較羅嗦,有的地方寫的也可能不對,希望大家指出,謝謝。下面還要對RCP部分進行一些描述。
GEF的一些資源:
1。www.eclipse.org 這個是源頭啊,faq也可以看看
2。ibm的dW 除了相關文章,這里還有本關于gef和emf的紅寶書,可以免費下載。
3。eclipseworld.org 這個是一個關于eclipse中文社區
4。八進制的blog http://bjzhanghao.cnblogs.com/ 。
5。這里還有些中文教程 http://www.benisoft.com/cn/index.htm
6。還有一些blog http://www.aygfsteel.com/hopeshared/ http://www.aygfsteel.com/eclipshine/ http://www.aygfsteel.com/reloadcn/ http://www.aygfsteel.com/USTCEric
好了,現在來介紹一下GEF的實現機制,主要是我的一些理解可能還有一不對的地方,歡迎大家幫我改正。
GEF是具有標準MVC(Model-View-Control)結構的圖形編輯框架,其中Model由我們自己根據業務來設計,它要能夠提供某種模型改變通知的機制,用來把Model的變化告訴Control層;Control層由一些EditPart實現,EditPart是整個GEF的核心部件;而 View層,大多數情況下,使用Draw2D實現了,其作用是把Model以圖形化的方式表現給使用者,當然你也可以使用其他圖形包,不過gef與 Draw2D的結合還是非常緊密的,一般為了使用方便,View都是使用Draw2D實現的。
關于Draw2D,八進制有一篇相應的文章,還有eclipse上的一篇文章,可以參考,這里有幾點需要強調一下:
1。Draw2D提供了很多缺省圖形,最常見的有三類:1、形狀(Shape),如矩形、三角形、橢圓形等等;2、控件(Widget),如標簽、按鈕、滾動條等等;3、層(Layer),它們用來為放置于其中的圖形提供縮放、滾動等功能。
2。每個圖形都可以擁有一個邊框(Border),Draw2D所提供的邊框類型有GroupBoxBorder、TitleBarBorder、ImageBorder、 ButtonBorder,以及可以組合兩種邊框的CompoundBorder等等,在Draw2D里還專門有一個Insets類用來表示邊框在圖形中所占的位置,它包含上下左右四個整型數值。
3。一個圖形可以包含很多個子圖形,這些被包含的圖形在顯示的時候必須以某種方式被排列起來,負責這個任務的就是父圖形的LayoutManager,相應的約束(Constraint)對象就在這里,比如在子圖形刷新(refreshVisuals)時可以通過將其布局約束傳遞給其父元素來定位。
4。利用Draw2D中的Router、Anchor和Locator,可以實現多種連接樣式。Router負責連接線的外觀和操作方式;Anchor控制連接線端點在圖形上的位置,即"錨點"的位置;Locator的作用是定位圖形。
前面說過了GEF是通過MVC架構實現的,下面分別介紹這三個方面:
一、模型(Model):這個是相對比較容易理解,模型保存著業務邏輯,他是GEF這中唯一可以持久化的,并且需要提供相應的監聽機制,來通知 EditPart去更新View,監聽機制又可以分為分布式和集中式兩種,我們可以針對域監聽器進行轉有通知,也可以全局廣播;在這里我們認為 Command(EditPart用來更新模型的對象)也是模型的一個部分,因為它了解模型。此外,在有時候可能需要多種模型。我們對于業務模型,提供一個“視圖”模型,這樣便于實現和滿足相應的要求,不管內部模型是怎么樣的,都要實現通知機制。
二、視圖。GEF提供了在EclipseWorkbench中使用Viewer,稱為EditPartViewer。它的作用和jface中的 Viewer很相,不過這時候它的ContentProvider和LabelProvider是EditPart,通過setContents設置(注意在八進制代碼中setContents參數為模型對象,通過分析發現,在AbstractEditPartViewer中此方法被重載,可以除了接受 org.eclipse.gef.editpart對象外可以接受Object對象,不過此時回調用EditPartFactory,通過給定的模型對象來創建相應的EditPart,個人認為這樣做并不與“View與Model無直接聯系”的要求相矛盾)。這個接口繼承了 org.eclipse.jface.viewers.ISelectionProvider接口(這個也和jface中的viewer一樣),因此使 viewer提供了監聽器,這樣可以將對view的操作傳遞到editpart。EditPartViewer會維護各個EditPart的選中狀態,如果沒有被選中的EditPart,則缺省選中的是作為contents的EditPart。
EditPartViewer可以適配到任何一個SWT控件,因為它要求有createControl方法,接受一個父SWT控件作為適配對象,從而將 GEF生成的Figure對象顯示在這個SWT控件上,因此GEF可以使用任何圖形包,不過GEF提供了對Draw2D的默認實現,比如 AbstractGraphicalEditPart類中的回調方法createFigure就要求返回一個IFigure對象。將一個GEF圖形顯示出來可以使用org.eclipse.ui.part.EditorPart也可以是org.eclipse.ui.part.ViewPart,如果使用 EditorPart,則需要實現繼承自WorkbenchPart的createPartControl,如果是ViewPart則需要自己實現與 GEF的交互,不過Eclipse Workbench對viewpart和editorpart的支持是一樣的,都是在plugin文件擴展相應的擴展點。一般來說,使用GEF對 Editorpart的模式實現是非常方便的。
GEF目前提供兩種視圖分別是GraphicalViewer和TreeViewer,都間接繼承了EditPartViewer接口,前者利用 Draw2D圖形(IFigure)作為表現方式,多用于編輯區域,后者則多用于實現大綱展示,并且他們有兩個具體的實現,分別是 ScrollingGraphicalViewer和TreeViewer。其中ScrollingGraphicalViewer,通過分析GEF源代碼可知,是GraphicalEditor中Viewer的默認實現,我們可以通過GraphicalEditor類提供的 setGraphicalViewer(GraphicalViewer)方法重新設置其他viewer。我們在使用的時候通過 configureGraphicalViewer和initializeGraphicalViewer方法來配置GraphicalViewer。所不同的是,在configureGraphicalViewer之后initializeGraphicalViewer之前插入了一個 hookGraphicalViewer方法,其作用是同步選擇和把 EditPartViewer作為SelectionProvider注冊到所在的site,使其成為SelectionProvider。
此外,GEF中還提供一種成為調色板(PaletteViewer)的視圖,它也是間接實現了EditPartViewer接口。因此,我也像配置GraphicalViewer一樣來配置PaletteViewer,類似的使用 configurePaletteViewer和initializePaletteViewer方法,其間類似的插入了一個 hookPaletteViewer方法,用于設置相應EditDomain的PaletteViewer。
最后,總結一下如何配置GraphicalView,主要是四個方面:
1。設置一個RootEditPart:RootEditPart的是使整個GEF框架運行起來的關鍵之一。RootEditPart并不對應于任何的模型對象,它將從setContents()方法中接收到的模型對象進行轉換,并添加到整個的EditPart體系中去。
2。設置其EditPartFactory:負責從模型到EditPart的轉換。一般來說一個模型對象對應于一個EditPart。
3。設置EditDomain:用來接收事件并選擇恰當的事件處理函數進行處理,這個主要是為了相應view所接收到的用戶的操作。
4。調用setContents()方法:為其設置要顯示的內容。
三、控制器(EditPart)。這是GEF最核心的地方了,它將model和view聯系起來。程序員可以繼承三種EditPart,分別是 org.eclipse.gef.editparts.AbstractGraphicalEditPart,org.eclipse.gef.editparts.AbstractConnectionEditPart(AbstractGraphicalEditPart的子類)和org.eclipse.gef.editparts.AbstractTreeEditPart。EditPart 的生命周期當setContents時,將模型對象輸入,此對象為頂層對象,通過它可以遍歷所有的模型對象。:EditPartViewer配備有 EditPartFactory,EditPartViewer通過EditPartFactory構造相應的Contents EditPart。然后EditPartViewer中的每個EditPart通過EditPartFactory構造相應的子EditPart。當用戶添加新的模型的時候,包含這些模型對象的所對應的EditPart將構造相應的EditPart作出相應。視圖的構造和EditPart的構造是同時進行的,每當EditPart構造并添加到它的父EditPart后相應的view也做同樣的事情,比如GraphicalEditor中就有 GraphicalViewer成員變量,在此edipart創建時回調用createPartControl,在這個方法中將會創建相應的view,并將其加入到父view中。
一旦用戶丟棄了某些模型對象,相應的EditPart就會被丟棄。比如用戶執行了刪除操作,則相應的模型對象和相應的EditPart都被刪除了,但用戶撤銷了刪除操作而重新創建的EditPart與原來的EditPart是不同的,所以EditPart是不能保存長期信息的,應該交給模型去保存,并且 EditPart也是不能被Command所引用。
在具體使用時,應該先定一個Contents EditPart,它的父元素為EditPartViewer的RootEditPart。RootEditPart是EditPartView的 root,它將EditPartView和它的contents(一般認為是頂層模型對象所對應的EditPart)聯系起來,RootEditPart 為用戶的EditPart提供一致性的上下文(context)。并且,當沒有被選中的模型EditPart時,頂層對象的EditPart是默認被選中的,而RootEditPart是不能被選中的或者定位的,甚至不能與用戶相交互,他也就沒有對應的模型對象。GEF提供了幾個默認的 RootEditPart的實現:FreeformGraphicalRootEditPart,RootTreeEditPart, ScalableRootEditPart,ScalableFreeformRootEditPart(此類繼承于 FreeformGraphicalRootEditPart,增加了縮放Zoom支持,提供了ZoomManager;增加了一個LayerPane: ScalableFreeformLayeredPane。),(原來還有個GraphicalRootEditPart,已經deprecated,用 ScalableRootEditPart代替)。這里要注意的是:
1)對于FreeformRootEditPart(以及它的子類 ScalableFreeformRootEditPart),它的Contents EditPart應該有一個org.eclipse.draw2d.FreeformFigure對象(比如FreeformLayer或FreeformLayeredPane)作為它的Figure。例如在我們的代碼里,對應的就是在 DiagramPart中的createFigure方法,它返回了一個FreeformLayer對象。
2) FreeformGraphicalRootEditPart的Primery Layer沒有使用draw2d的LayoutManager,并且,除非contents的figure是個freeform figure,否則不能正確的調整大小。
3)FreefromGraphicalRootEditPart使用FreeformViewport類作為它的primary figure,這個類必須和ScrollingGraphicalViewer使用。
4)ScalableRootEditPart使用 Viewport作為primery figure,這個類必須和ScrollingGraphicalViewer使用。
5)RootTreeEditPart是TreeViewer的RootEditPart。
Contents EditPart的圖形一般是一個空面板,該面板將包含diagram的子圖。
我們還要定義其他“視圖”模型的EditPart,比如節點(node),連接(connection)。對于節點,是Graphical,可以繼承 AbstractGraphicalEditPart。并且可以實現NodeEditPart接口,則可以支持源和目標的 ConnectionEditPart,使用Anchor,具體在下面有講述。在這里,我們的代碼的NodePart類中,重載了 refreshVisual方法,其中設置figure的布局約束是通過它的父元素來定位的,我不知道這是不是標準的方法,XYLayout使用的是 rectangle約束,如果長寬設為-1,表示為該圖形提供理想大小。不要使用setBounds,XYLayout布局管理器可以保證對滾動條進行更新,并能將相對約束轉化為絕對約束,如果長寬設置為-1,則能夠將圖形設置為理想的大小。此外,為了改進性能,我們可以將refreshVisual中有若干模型的分別刷新,或者設置是否需要刷新的開關。
對于連接,可以繼承AbstractConnectionEditPart,這個抽象類已經實現了 ConnectionEditPart。和Node實現一樣,通過refreshVisual方法將屬性從模型映射到圖形。連接也可以有約束,不過與前面的約束不盡相同。在這里,連接路由器(connection router)使用約束來是連接轉向(bend)。需要注意的是,一個Connection EditPart的圖形必須是一個draw2d的Connection,這樣就引入了連接錨(Connection Anchor)。連接必須由連接錨(ConnectionAnchor)“錨定”在兩端。因此,需要在連接EditPart或者節點EditPart中實現使用什么樣的錨。缺省情況下,GEF使用節點EditPart通過實現NodeEditPart接口來提供錨。這是因為,錨的選擇取決與各個節點上正在使用的圖形,而連接EditPart不應了解節點上的圖形,并且,當用戶創建連接的時候,連接EditPart并沒有創建,這時候就由節點自己顯示反饋。這里需要注意的是:何時使用以及何時不使用ConnectionEditPart,當用戶可以選擇某些東西并可與之交互時,就需要 ConnectionEditPart。它可能與某行中的某個對象相對應,并且回自己刪除;如果這是需要一個繪制直線的節點或者容器,那么只需要用圖形的 paint方法繪制直線,或者組合一個圖形,這個圖形包含Polyline圖形。連接必須要擁有源和目標。如果要創建一個連接,并且在沒有源和目標存在的情況下,可以繼承AbstractGraphicalEditPart,并使用連接圖形,比如PolylineConnection。
正如前面說的,模型需要實現某種通知機制,各個EditPart要將自己做為監聽者,注冊在模型上,以便在模型發生改變的時候收到相應的通知。 當EditPart收到相應的通知時,調用相應的處理方法,來進行一次強制的刷新,對于模型對象的增減可以調用refreshChildren方法,對于簡單屬性的更改可以調用refreshVisuals方法,這時,像前面提到的,可以只刷新改變的部分,以提高性能。一般,我們在Activate方法中注冊監聽器,在Deactivate方法中注銷監聽器。
以上完成的是從模型到視圖的映射,你還需要實現的是編輯策略Edit Policies,因為EditPart并不直接對模型進行編輯,每一個EditPolicy都針對一個特定的編輯任務或者一組相關的工作。這樣的實現便于在不同的EditPart間重用(reuse)編輯行為,并且這樣的實現,可以動態的改變行為,比如改變layout或者routing時。在 createEditPolicies方法中,我們要insall我們的的Policies,這時候就需要roles,roles的作用就是作為關鍵字來標示不同的policies。policy中提供Command,每個應用程序都有個Command棧,Command的運行要通過Command棧,而不是直接調用execute方法。
說到這里,要注意EditPartView是視圖的,一般放在一個UI插件上比如EditorPart,viewPart,甚至swt控件。EditPart是控制器,聯系視圖和模型。
在本文的最后介紹一下GEF的運行機制:EditPartView接受用戶的操作,如節點的移動,增刪,每個節點對應一個EditPart對象,每個 EditPart都有一組由Role區分的EditPolicies,每個EditPolicy會對應一些Command對象,Command最終會對模型進行操作。用戶的操作會轉化為Request分配給特定的EditPolicy,由EditPolicy創建相應的Command對模型進行操作,這些 Command會保留在EditDomain的命令堆棧中,用于實現undo,redo功能,EditDomain是專門用于維護 EditPartView,Command信息的對象,一般每個EditPart有一個EditDomain。
以上就是對所作的GEF部分的大概描述,可能有些地方寫的比較羅嗦,有的地方寫的也可能不對,希望大家指出,謝謝。下面還要對RCP部分進行一些描述。