月蝕傳說

          浮躁讓人失去理智
          posts - 25, comments - 101, trackbacks - 0, articles - 0
            BlogJava :: 首頁 ::  :: 聯系 :: 聚合  :: 管理

          能夠拖動的矩形

          Posted on 2006-10-09 18:59 Dart 閱讀(1695) 評論(6)  編輯  收藏 所屬分類: GEF

          這章我將說一下如何去實現一個數據表在編輯器上顯示,并且能夠進行位置和尺寸的改變。我們將涉及到的內容有:Figure,EditPolicy,Command。示例代碼下載

          1.在Editor上實現一個簡單的數據表

          上一章中,我們實現了一個空的Editor,畫布上什么都沒有,今天我會讓它顯示點東西。
          我們已經說過了,Editor要顯示一個圖形,是根據我們給Viewer設置的模型,找到對應的EditPart,然后調用EditPart的createFigure得到圖形,最后繪制在Editor上,大致如下:

          http://album.sina.com.cn/pic/5414465b02000419



          我們需要做的事情就讓我們的TableEditPart復寫基類的createFigure方法,然后返回一個Figure圖形,最后再在代碼中更改我們最初設置的模型內容。

          創建TableFigure

          一般來說,當我們需要生成一個Figure圖形的時候,最好的辦法就是從Figure類繼承一個新的類出來,然后復寫Figure的paintFigure方法,繪制出我們想要的圖案。見下代碼:

          public ? class ?TableFigure?extends?Figure?{

          ????
          protected ? void ?paintFigure(Graphics?graphics)?{
          ????????super.paintFigure(graphics);
          ????????
          // ?得到Figure的Bounds
          ????????Rectangle?bounds? = ?getBounds();
          ????????
          // ?在它周圍繪制一個矩形,寬度和高度稍微小一點,以便能全部顯示
          ????????graphics.drawRectangle(bounds.x,bounds.y,bounds.width? - ? 1 ?,?bounds.height? - ? 1 );
          ????}
          }


          TableFigure 顯示的是一個矩形圖形。getBounds方法是得到的是Figure的范圍對象,這個對象是一個Recangle類型,包括有圖形的坐標(Point),以及尺寸(Dimension),而這個范圍對象并不是我們去指定的,而是根據TableFigure的父Figure來給它設定的。這里需要說一下,在GEF中,EditPart在繪制Figure的時候,步驟是先繪制好自己的Figure,然后查看自己的子EditPart,獲得他們的Figure,確定他們的大小位置(根據默認或者是本身Figure的布局管理器來得到)繪制在自身Figure之上,也就是說,這是一個遞歸的過程。

          修改TableEditPart代碼

          我們已經生成了一個TableFigure類,現在我們需要讓TableEditPart的createFigure方法返回這個類的一個實例。

          public ? class ?TableEditPart?extends?DBEditPartBase?{

          ????
          protected ?IFigure?createFigure()?{
          ????????
          // ?返回Table的Figure
          ???????? return ? new ?TableFigure();
          ????}
          }


          重新設置模型

          我們創建Editor的時候,在初始化Viewer的方法中,生成了一個Schema模型給它,現在我們需要把我們的Table模型添加上去,也就是說,讓新的Table模型作為Schema的子對象添加進去

          protected ? void ?initializeGraphicalViewer()?{
          ????????
          // ?硬編碼生成一個數據庫模型
          ????????
          // ?這個數據庫中有一個表
          ????????Schema?schema? = ? new ?Schema();
          ????????Table?table?
          = ? new ?Table();
          ????????
          ????????schema.addChild(table);
          ????????
          this .getGraphicalViewer().setContents(schema);
          ????}


          可以看得出來,我們其實只是修改了我們的模型,但是圖形顯示是和EditPart相關的,SchemaEditPart是如何得知它會擁有一個子EditPart —— TableEditPart的呢?仔細看下DBBaseEditPart就可以很清楚了。
          由于我們復寫了EditPart的getModelChildren,返回的是DBBaseEditPart對應模型的子模型,所以EditPart就可以通過這些子模型來得到子編輯單元(Chilren EditPart)

          ???? protected ?List?getModelChildren()?{

          ????????
          if (getModel()?instanceof?DBBase){
          ????????????
          return ?((DBBase)getModel()).getChildren();
          ????????}
          ????????
          return ?super.getModelChildren();
          ????}


          最后讓我們運行一下,得到了一個繪制有矩形的Editor:

          http://album.sina.com.cn/pic/5414465b0200041a



          2.初步討論EditPolicy

          在剛才我們實現的編輯其中,已經將Table模型的圖形顯示了出來,但是僅僅只是有一個矩形畫在畫布上,當我們點擊它的時候沒有任何反應,好像完全只是一張靜態的圖片,我們要的效果的并不是這個,而是需要一個能夠對其操作的圖形。

          第一章里我簡單地提到了在GEF中事件處理的過程,看看前面的文章可以知道,我們想要能夠對我們顯示的TableFigure圖形進行編輯,需要的是一個能夠處理相關Request的EditPolicy。

          EditPolicy的類型有很多,大致分為圖形相關和圖形無關兩大類,我在這個例子中使用到了這幾個圖形相關的EditPolicy:LayoutEditPolicy ,NonResizeableEditPolicy。

          LayoutEditPolicy 一般是作為父EditPart所具有的EditPolicy,就是說,如果我們的Figure需要對它的子EditPart進行一些圖形方面管理的話,使用這個EditPolicy比較合適。它能夠處理一些容器類EditPart的應該具備的操作:增加一個EditPart,刪除一個EditPart,移動子EditPart對應圖形等。

          并且它還能夠為它的子EditPart設置對應的EditPolicy,這樣一來就統一了子EditPart的一些行為。一會我講具體說一下這個問題。

          NonResizeableEditPolicy,顧名思義,它是一個不處理尺寸變化的EditPolicy,但是它能夠處理EditPart對應Figure位置變化,由于我們的數據表的大小需要根據它所擁有列來決定,外界不應該對它的尺寸進行修改,所以我在這里選用了它。

          怎么使用EditPolicy呢?EditPolicy是被"安裝"到EditPart上的,在EditPart中,有一個接口方法:createEditPolicies,我們要在這里面進行安裝。安裝EditPolicy使用installEditPolicy方法。

          回過頭再看看我們所要用的這兩個EditPolicy,他們兩個都應該安裝到哪個EditPart上呢。很顯然,LayoutEditPolicy應該安裝到父EditPart,也就是SchemaEditPart,這樣一來,SchemaEditPart就能對TableEditPart進行管理了; NonResizeableEditPolicy就應該屬于TableEditPart,我們需要用它來改變TableEditPart對應Figure 的位置。

          當然,LayoutEditPolicy和NonResizeableEditPolicy不能實例化后直接安裝到 EditPart上,因為我們還需要復寫他們的一些方法,稍后我們會提到。我們先創建兩個類,分別繼承LayoutEditPolicy, NonResizeableEditPolicy:

          public ? class ?SchemaLayoutEditPolicy?extends?LayoutEditPolicy?{

          ????
          protected ?EditPolicy?createChildEditPolicy(EditPart?child)?{
          ????????????? ?
          return ? null ;
          ????}
          ????
          protected ?Command?getCreateCommand(CreateRequest?request)?{
          ????????????? ?
          return ? null ;
          ????}

          ????
          protected ?Command?getDeleteDependantCommand(Request?request)?{
          ????????????????
          return ? null ;
          ????}

          ???
          protected ?Command?getMoveChildrenCommand(Request?request)?{
          ????????????? ?
          return ? null ;
          ????}

          }

          ?

          public ? class ?TableNonResizableEditPolicy?extends?NonResizableEditPolicy?{

          }


          很明顯,這輛個類只是繼承了基類,并沒有復寫或實現基類的方法。
          姑且這樣,我們先把SchemaLayoutEditPolicy安裝到SchemaEditPart上:

          ???? protected ? void ?createEditPolicies()?{
          ????????
          this .installEditPolicy(EditPolicy.LAYOUT_ROLE, new ?SchemaLayoutEditPolicy());
          ????}

          installEditPolicy 的第一個參數其實并沒有什么用,這個參數是一個String類型,隨便寫個字符串也不會影響到我們EditPolicy的安裝以及它的工作的(也有人說,如果第一個參數重復的話,EditPolicy會被覆蓋掉。我沒有研究過,各位朋友可以試一下)。

          好了,SchemaEditPart所需要的EditPolicy已經搞定,剩下TableEditPart了。大家可能會認為,安裝它的EditPolicy也和 SchemaEditPart一樣,復寫createEditPolicies方法,然后installEditPolicy即可。是的,這樣沒有問題,但是由于我們的SchemaLayoutEditPolicy中有這么一個接口方法:createChildEditPolicy,這就是我在前面所說的,為了統一管理,給子EditPart安裝所對應的EditPolicy。雖然這樣做和直接安裝的效果差不多(應該是一樣,但是也有可能有一些差別),但是我認為還是把子EditPolicy的安裝交給父EditPolicy吧:

          ???? protected ?EditPolicy?createChildEditPolicy(EditPart?child)?{
          ????????
          if (child?instanceof?TableEditPart)? return ? new ?TableNonResizableEditPolicy();
          ????????
          return ? new ?NonResizableEditPolicy();
          ????}

          好了,讓我們運行一下。呵呵,是不是可以點擊我們的“數據表”了。但是這樣還是不能移動我們的數據表。

          3. 修改我們的類 ;Command 的使用


          第一章我已經講過了,我們的模型有時需要添加一些和模型本身無關但和圖形有關的屬性。因為這樣一來我們就能夠記錄我們的圖形發生的位置變化,再通過模型的改變去通知EditPart刷新我們的圖形。
          我們先為Table增加一個屬性:location

          ???? protected ?Point?location? = ? new ?Point( 0 , 0 );
          ????
          /* *
          ?????*?@return?返回?location.
          ?????
          */
          ????
          public ?Point?getLocation()?{
          ????????
          return ?location;
          ????}
          ????
          /* *
          ?????*?@param?location?設置?location?
          ?????
          */
          ????
          public ? void ?setLocation(Point?location)?{
          ????????Point?old?
          = ? this .location;
          ????????
          this .location? = ?location;
          ????}


          這個屬性代表了圖形目前所在的位置坐標。

          有朋友要問:這里只是有了屬性,那當屬性改變的時候怎么去通知呢?我記得我也在第一章講了,一般的做法是為我們的模型增加一個屬性更改的事件發生源:PropertyChangeSupport

          我們把事件發生源寫到基類DBBase中,并增加幾個方法去發送事件以及添加刪除監聽器:

          ???? public ? static ?final?String?PRO_FIGURE? = ? " __figure__property " ;
          ????
          ????
          private ?PropertyChangeSupport?support? = ? new ?PropertyChangeSupport( this );

          ?
          public ? void ?addPropertyChangeListener(PropertyChangeListener?l){
          ????????support.addPropertyChangeListener(l);
          ????}
          ????
          ????
          public ? void ?removePropertyChangeListener(PropertyChangeListener?l){
          ????????support.removePropertyChangeListener(l);
          ????}
          ????
          ????
          public ? void ?fireFigurePropertyChange(Object?old,Object?now){
          ????????support.firePropertyChange(PRO_FIGURE,old,now);
          ????}


          好了,我們的事件源做好了,下面該想想讓誰去監聽了。

          毫無疑問,我們的監聽器應該是DBBaseEditPart,因為它才有能力去刷新Figure,所以我們需要更改DBBasEditPart代碼,如下:

          public ? class ?DBEditPartBase?extends?AbstractGraphicalEditPart?implements?PropertyChangeListener{
          ????
          public ? void ?activate()?{
          ????????
          if (getModel()? != ? null ? && ?getModel()?instanceof?DBBase){
          ????????????((DBBase)getModel()).addPropertyChangeListener(
          this );
          ????????}
          ????????super.activate();
          ????}
          ?????
          public ? void ?deactivate()?{
          ????????
          if (getModel()? != ? null ? && ?getModel()?instanceof?DBBase){
          ????????????((DBBase)getModel()).removePropertyChangeListener(
          this );
          ????????}
          ????????super.deactivate();
          ????}
          ????
          public ? void ?propertyChange(PropertyChangeEvent?evt)?{
          ???????String?pName?
          = ?evt.getPropertyName();
          ???????
          if (pName.equals(DBBase.PRO_FIGURE)){
          ???????????
          this .refreshVisuals();
          ???????}
          ????}
          }


          大家注意下propertyChange方法,當我們在獲得事件類型為PRO_FIGURE后,就會直接去調用refreshVisuals去刷新我們的Figure。
          但是refreshVisuals其實是空方法,它什么都沒有做!

          所以我們必須在TableEditPart中要復寫它:

          ??? protected ? void ?refreshVisuals()?{
          ????????super.refreshVisuals();
          ????????
          // ?得到當前Figure的位置和大小
          ????????Rectangle?rect? = ? this .getFigure().getBounds();
          ????????
          ????????
          // ?獲得更改后的位置
          ????????Point?p? = ?((Table)?getModel()).getLocation();
          ????????
          ????????
          // ?我們只更改Table的位置
          ????????((GraphicalEditPart)? this .getParent()).setLayoutConstraint( this ,? this
          ????????????????.getFigure(),
          new ?Rectangle(p,?rect.getSize()));
          ????}

          最后一句代碼是什么含義呢?這是讓TableEditPart去找到它的父EditPart,也就是SchemaEditPart,再讓它去“約束” TableEditPart圖形的位置和大小,當然了,我們這里沒有改變大小,只是通過Table模型的Location屬性去更改它的位置而已。當調用了setLayoutConstraint方法后,我們的圖形就會自動進行重繪。

          接下來,讓Table模型中更改location屬性時將更改事件發送出來,以便EditPart能夠截獲并處理:

          ????public?void?setLocation(Point?location)?{
          ????????Point?old?
          =?this.location;
          ????????
          this.location?=?location;
          ????????
          this.fireFigurePropertyChange(old,this.location);
          ????}


          我們已經做了很多調整,改了不少代碼了,這會運行看看吧!

          對不起,我們的TableFigure還是不會移動!
          這是由于我們忘記了在TableNonResizeableEditPolicy中做文章。

          剛才已經說過了,TableNonResizeableEditPolicy能夠處理對圖形移動的,但是它只是通知我們圖形移動了,要找我們索取一個 Command去執行這種變化。所以我們還需要寫一個Command類,讓這個Command去執行對模型位置的更改(關于Command的介紹請回過頭看第一章):

          public?class?TableMoveCommand?extends?Command?{

          ????
          private?ChangeBoundsRequest?request;
          ????
          ????
          private?Table?model;

          ????
          public?void?execute()?{
          ??????Point?old?
          =?getModel().getLocation();
          ??????
          int?x?=?request.getMoveDelta().x;
          ??????
          int?y?=?request.getMoveDelta().y;
          ??????
          ??????getModel().setLocation(
          new?Point(old.x+x,old.y+y));
          ????}
          }

          然后我們在TableNonResizeableEditPolicy中復寫getMoveCommand方法:

          ?protected?Command?getMoveCommand(ChangeBoundsRequest?request)?{

          ????????TableMoveCommand?command?
          =?new?TableMoveCommand();
          ????????command.setModel((Table)getHost().getModel());
          ????????command.setRequest(request);
          ????????
          return?command;
          ????}

          好了!這會再運行看看,是不是能移動了?

          4.結束語

          我們今天把上次例子代碼進行了一些修改,得到了一個能夠隨意改變位置的矩形,其中主要簡單地講述了一些EditPolicy如何使用。
          本人文筆比較爛,說事情總說不清,如果有不清楚的地方請留言,我會進行修改,盡量讓大家都能看懂。
          以后的章節,我們會繼續討論EditPolicy以及Figure布局等問題。

          評論

          # re: 能夠拖動的矩形  回復  更多評論   

          2007-01-12 16:57 by 過客[匿名]
          編輯器中,通過實現PaletteRoot工具,可以將自己定義的模型拖入到編輯器中,該模型以圖形化的形式在編輯器中顯示

          那如何將視圖中TreeViewer的節點(每個節點都有相應的模型對應)拖入到編輯器中呢?

          # re: 能夠拖動的矩形  回復  更多評論   

          2007-03-13 15:58 by 匿名
          根本允許起來后看不到矩形啊

          # re: 能夠拖動的矩形  回復  更多評論   

          2007-03-13 17:16 by Dart
          先確認,你用的Eclipse和GEF是什么版本的,這篇文章寫得比較老了,Eclipse版本應該是3.0.1,GEF版本也應該是對應當時Eclipse版本的.
          代碼應該不會有問題,因為在這篇文章還沒有丟失之前很多人都已經用過了

          # re: 能夠拖動的矩形  回復  更多評論   

          2007-03-13 17:33 by 匿名
          在3.2上面能運行起來嘛,第五篇的代碼可以運行,但第二章的運行了沒有看不到矩形.

          # re: 能夠拖動的矩形  回復  更多評論   

          2007-03-14 13:01 by Dart
          3.2可能跑不起來,我沒試過

          # re: 能夠拖動的矩形  回復  更多評論   

          2007-04-06 12:54 by xx
          看不到矩形是因為沒有設置矩形大小,默認的大小為0,0,看不到而已
          主站蜘蛛池模板: 曲周县| 华坪县| 双柏县| 县级市| 松桃| SHOW| 东阳市| 雷波县| 江永县| 广汉市| 尤溪县| 萝北县| 文水县| 吴忠市| 安仁县| 呼图壁县| 合作市| 广德县| 潞城市| 石渠县| 南靖县| 肇庆市| 贡嘎县| 南宫市| 巨野县| 巫山县| 城步| 抚远县| 临海市| 云霄县| 蒙山县| 腾冲县| 米脂县| 阿克苏市| 临洮县| 澄江县| 邓州市| 芜湖市| 如皋市| 博客| 浪卡子县|