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