離開GEF項(xiàng)目組已經(jīng)一個(gè)多月了,但是那段日子給我很深的印象,因?yàn)槟欠路鹗俏艺嬲露Q心,學(xué)習(xí)成為一個(gè)好的IT人員之路的開始。而且開始積累編程經(jīng)驗(yàn)。GEF并不簡(jiǎn)單,面對(duì)這個(gè)攔路虎,我很是興奮,想把那段時(shí)間的學(xué)習(xí)體會(huì)以及我們的項(xiàng)目來(lái)個(gè)大致的介紹。
我們的項(xiàng)目是一個(gè)Eclipse平臺(tái)上的用于本體建模的圖形編輯器,大家決定采用GEF框架,因?yàn)樗浅_m合開發(fā)帶有調(diào)色板的以編輯器為主要部件的應(yīng)用程序。剛開始一個(gè)星期,非常迷茫,不知從何下手。這里要非常感謝八進(jìn)制的blog,他寫的關(guān)于GEF的系列給了我很大的幫助,至今我還有很多沒(méi)有完全消化的地方。
一. 入門
我們選取了一個(gè)GefPractice的例子作為學(xué)習(xí)的對(duì)象。它的結(jié)構(gòu)很清楚,包劃分得很容易理解(現(xiàn)在發(fā)現(xiàn)包的劃分基本是按照gef api中包的劃分來(lái)的)。它實(shí)現(xiàn)了一個(gè)簡(jiǎn)單得編輯器的功能,可以創(chuàng)建節(jié)點(diǎn)、編輯節(jié)點(diǎn)和刪除節(jié)點(diǎn),還可以創(chuàng)建節(jié)點(diǎn)之間的連接。它一共劃分了10個(gè)包:
1. ui包:這里是用戶界面,主要是編輯器editor,編輯器一般是繼承GraphicalEditorWithPalette,這樣我們就可以得到一個(gè)GraphicalViewer和一個(gè)PaletteViewer。我認(rèn)為學(xué)習(xí)從ui入手比較簡(jiǎn)單,因?yàn)槲覀円话愫?/SPAN>editor接觸比較多,對(duì)于GEF內(nèi)部工作機(jī)制的了解也應(yīng)從editor開始,這樣更直觀。Editor中有幾個(gè)重要的概念:
① 在editor的構(gòu)造函數(shù)中,調(diào)用setEditDomain()方法,而使用的一般就是DefaultEditDomain(),它是EditDomain的一個(gè)默認(rèn)實(shí)現(xiàn)。它是一個(gè)GEF程序的狀態(tài)集合,包括命令棧,一個(gè)或多個(gè)EditpartViewer,和當(dāng)前的活動(dòng)工具。
② 兩個(gè)方法:configureGraphicalViewer()和initialGraphicalViewer(). configureCraphicalViewer()方法中替graphicalViewer設(shè)置rootEditPart,設(shè)置EditPartFactory。InitalGraphicalViewer()方法中,替graphicalViewer設(shè)置contentProvider,一般是畫布(model包中),設(shè)置拖放監(jiān)聽器(這個(gè)以后還要重點(diǎn)說(shuō)明)。
③ GetAdapt()方法,應(yīng)該是視圖之間的切換(這個(gè)現(xiàn)在還不太清楚)。
④ 內(nèi)部類繼承ContentOutlinePage實(shí)現(xiàn)大綱視圖。
2.model包:
模型:GEF的模型只與控制器打交道,而不知道任何與視圖有關(guān)的東西。為了能讓控制器知道模型的變化,應(yīng)該把控制器作為事件監(jiān)聽者注冊(cè)在模型中,當(dāng)模型發(fā)生變化時(shí),就觸發(fā)相應(yīng)的事件給控制器,后者負(fù)責(zé)通知各個(gè)視圖進(jìn)行更新。
類之間的層次關(guān)系
在org.sklse.oed本題編輯器項(xiàng)目中,創(chuàng)建了org.sklse.oed.model包來(lái)裝載模型,在設(shè)計(jì)中,我們結(jié)合java程序面向?qū)ο蟮奶攸c(diǎn),將創(chuàng)建的類抽象成三個(gè)層次,如下圖:
AbstractModel作為整個(gè)模塊的根類,實(shí)現(xiàn)模型層接點(diǎn)和連線公用的監(jiān)聽接口,屬性變化監(jiān)聽事件。主要是通過(guò)implements IpropertySource來(lái)實(shí)現(xiàn)的,IpropertySource是eclipse中關(guān)于屬性視圖開發(fā)方面的接口。
結(jié)點(diǎn)父類:AbstractNodeModel.class
該類抽象出結(jié)點(diǎn)公用的一些屬性和方法,比如:定義了P_CONSTRAINT、P_TEXT、P_SOURCE_CONNECTION、P_TARGET_CONNECTION等屬性,其中P_CONSTRAINT用來(lái)布局的,P_TEXT是模型的名字,而P_SOURCE_CONNECTION、P_TARGET_CONNECTION用來(lái)存放模型的源連接和目的連接的鏈表。
然后根據(jù)這些屬性定義一系列相關(guān)的操作,比如:
public void addSourceConnection(Object connx) {
sourceConnections.add(connx);
firePropertyChange(P_SOURCE_CONNECTION, null, null);
}
上面就是一個(gè)很典型的方法,目的是給模型對(duì)象添加一個(gè)源連接,參數(shù)connx表示源連接,sourceConnections.add(connx);是源鏈表的添加方法,firePropertyChange(P_SOURCE_CONNECTION, null, null)方法來(lái)激活屬性變化事件。正如mvc模式的思想一樣,在模型被修改的同時(shí),通過(guò)消息事件通知控制層(firePropertyChange),后者根據(jù)模型變化來(lái)修改視圖。
連線父類:AbstractConnectionModel.class
該類抽象出連線公用的一些屬性和方法,連線最重要的屬性就是P_BEND_POINT ,我把它理解成拐點(diǎn),即可以通過(guò)拖動(dòng)這個(gè)拐點(diǎn)來(lái)折變連線。一條連線就必須有源接點(diǎn)和目的接點(diǎn),即source, target,在model層增加一個(gè)連線,也就等于在source和target的源接點(diǎn)連表和目的接點(diǎn)鏈表上增加這條記錄。有這類方法:
public void attachSource() {
if (!source.getModelSourceConnections().contains(this))
source.addSourceConnection(this);
}
對(duì)于P_BEND_POINT屬性的變化也必須有一套方法:
public void addBendpoint(int index, Point point) {
bendpoints.add(index, point);
firePropertyChange(P_BEND_POINT, null, null);
}
畫板模型:ContentModel.class
編輯區(qū)的畫板模型,相當(dāng)于母版,其他圖形都是添加到它上面的。
//定義一個(gè)屬性
public static final String P_CHILDREN = "_children";
//定義一個(gè)存放孩子接點(diǎn)的鏈表
private List children = new ArrayList();
public void addChild(Object child) {//添加孩子結(jié)點(diǎn)
children.add(child);
firePropertyChange(P_CHILDREN, null, null);//激活屬性變化事件
}