程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Eclipse向導機制擴展 - 實現可定制的向導

Eclipse向導機制擴展 - 實現可定制的向導

編輯:關於JAVA

實現動態頁數和樹狀拓撲結構的 Eclipse 向導

本文首先剖析了 Eclipse 向導機制的實現原理,包括頁面的加載和注銷,頁面校驗,向導執行流程, 向導目標操作等。目前,Eclipse 向導頁面內容是固定的,向導一經創建,其內容和順序就無法改變。然 而,在現實世界中,我們面對的邏輯往往是復雜多變的,固定的順序的 Eclipse 向導經常無法滿足用戶 需要,因而,文章從兩個方面對當前 Eclipse 向導機制進行了擴展:具有動態頁數的 Eclipse 向導以及 樹狀拓撲結構的 Eclipse 向導。擴展後的 Eclipse 向導,可以很方便地被開發人員重用,進而提高開發 效率。

引言

文章將對現有 Eclipse 向導機制進行一系列的擴展,並提供相應的參考實現,用戶只需要在擴展的基 礎上增加自己的業務邏輯,便可以快捷地創建功能更為復雜的 Eclipse 向導。

如不做特殊說明,本文默認的開發和運行環境是:IBM JDK 1.6,Eclipse 3.4.x

Eclipse 向導機制介紹

在 Eclipse 中,向導是一種很好的輔助用戶操作的機制,通常用於指導用戶完成特定的業務操作,例 如:創建一個 Java 工程,導入一個文件,導出一個 JAR 文件等等,在 Eclipse 中都有相應的向導輔助 用戶完成。此外,在 Eclipse 插件開發中,向導也用得非常普遍,Eclipse 提供的向導框架可以幫助用 戶快速創建自己的向導。

向導基本操作和運行機制

開始之前,我們先來了解一下 Eclipse 向導的實現機制。只有對 Eclipse 向導的實現機制有深入了 解,才能實現合理的擴展。 Eclipse 向導由一系列的向導頁組成,通常用戶的操作會被分散到各個向導 頁,每個向導頁用於配置操作所需的某一方面的信息,當所有向導頁的配置完成之後,整個向導便可以執 行最後的操作並結束。此外,在向導設計中,用戶需要將當前向導頁所有必需信息填好,“Next”按鈕才 生效,從而進入下一個向導頁。當所有向導頁的內容填充完畢並且到達最後一個向導頁時,向導的 “Finish”按鈕才能有效,這時候點擊“Finish”按鈕開始執行向導的目標操作。

以 Eclipse 中創建一個工程的向導為例,打開 Eclipse 開發環境,選擇“File->New- >Project”,便會彈出一個創建新工程的向導,如圖 1 所示。可以看到,該向導中有兩個頁面,第一 個頁面用於選擇需要創建的工程類型,我們選擇“General”中的“Project”創建一個 Eclipse 普通工 程,第二個頁面用於配置創建 Eclipse 普通工程所需要的信息:包括工程名,工程位置以及工程所屬的 工作集。只有在第一頁中選好需要創建的工程類型之後,“Next”按鈕才有效,進而進入第二頁,當第二 頁中所有必填信息填完之後,“Finish”按鈕才有效,點擊它將開始創建一個 Eclipse 工程的操作。

圖 1. Eclipse 中創建普通工程向導

在 Eclipse 中,向導必須實現接口 org.eclipse.jface.wizard.IWizard,Eclipse 提供的 org.eclipse.jface.wizard.Wizard 是 IWizard 的一個抽象實現。向導頁必須實現接口 org.eclipse.jface.wizard.IWizardPage,org.eclipse.jface.wizard.WizardPage 是 IWizardPage 的 一個抽象實現。用戶如果想使用 Eclipse 提供的框架創建自己的向導,那麼向導和向導頁需要分別繼承 Wizard 類和 WizardPage 類並重寫相應方法。

創建一個 Eclipse 向導後台發生的操作順序大致如下,在這個過程中,我們需要重寫第一步中向導的 構造函數,以及第三步中添加向導頁操作。

1. 使用 IWizard 子類的構造函數創建 Eclipse 向導;

2. 創建向導所在的 Shell;

3. 添加向導頁:addPages(),重寫該方法給向導插入的向導頁;

4. 創建向導頁容器:WizardDialog.createPageContainer(Composite parent);

5. 創建每個向導頁對應的 Control:IWizard.createPageControls(Composite pageContainer)。 Eclipse 調用它來實例化所有的向導頁面。重寫它給向導添加持續可視的窗體小部件;

6. 顯示起始向導頁:調用 WizardDialog.showStartingPage():重寫該方法以獲取向導第一個頁面。

向導頁定制

下面我們來看一下向導頁的定制,向導頁擴展了 WizardPage 類。開發人員為了定制自己的頁面,必 須重寫下面一些方法:

Constructor:實例化向導頁面。

dispose():向導關閉時調用該方法清除向導頁相關對象。

createControl(Composite parent):重寫它來創建向導頁控件。

IWizard getWizard():獲取向導頁所在的向導對象。

setPageComplete:當該向導頁中的所有必須的設置都完成之後,可以將該頁的狀態設置為結束,然後 用戶可以進入下一頁。

setTitle(String title):設置向導頁標題。

setDescription(String description):設置向導頁的描述。

setImageDescriptor(ImageDescriptor image):提供頁面右上方出現的圖片。

setMessage(String message):設置向導頁中的提示信息。

setErrorMessage(String error): 設置向導頁中的錯誤提示信息。

performHelp():設置向導頁的幫助信息。

當向導中有多個向導頁時,我們可以在向導對話框最下方看到“Back”,“Next”,“Finish”和 “Cancel”四個按鈕。 “Back”,“Next”按鈕用於在多個向導頁間進行跳轉,當向導中只有一個向導 頁時,只有“Finish”和“Cancel”兩個按鈕。

“Back”按鈕:用於返回到前一個向導頁。點擊該按鈕,將調用函數 IWizardPage getPreviousPage (IWizardPage previousPage),返回前一個頁面。

“Next” 按鈕:用於進入下一個向導頁。對於一個設計合理的向導,只有當前向導頁中所有必填的信 息設置完成之後,“Next”按鈕才能變成有效狀態,然後用戶才被允許進入下一個頁面。當用戶填完頁面 中的必須信息時,程序將執行 setPageComplete(true) 方法將當前向導頁狀態設為完成狀態;之後用戶 點擊“Next”按鈕,向導頁 IWizardPage 的方法 getNextPage(IWizardPage nextPage) 被調用,返回下 一個向導頁。默認情況下,用戶將進入向導類 Wizard 的 addPages() 方法所提供的數組中的下一個頁面 。如果我們要實現下一頁有多種方案,必須重寫該方法來計算後一個頁面。

“Finish” 按鈕:當所有向導頁中的信息已經配好之後,用戶點擊 Finish 按鈕,調用 performFinish() 函數來執行向導目標操作,用戶需要重寫它來實現向導的業務邏輯,如果 performFinish() 執行業務邏輯失敗,則應該返回 false。

“Cancel”按鈕:在向導運行過程中,用戶可以隨時點擊該按鈕,退出向導。點擊該按鈕,將會調用 performCancel() 函數,取消當前向導操作,並將之前所做的操作回滾。

Eclipse 向導擴展

從對 Eclipse 向導機制分析可知,Eclipse 向導頁的添加是線性的,向導頁的內容也是固定的,頁面 內容和順序一旦確定就無法改變。在實際應用中,我們面對的需求往往是復雜的,用戶的操作步驟經常是 有分支的,向導頁的內容有時候根據用戶的操作需要動態變化。下面我們從兩個方面對當前 Eclipse 向 導機制進行擴展:動態頁數的 Eclipse 向導以及樹狀拓撲結構的 Eclipse 向導。

動態頁數的 Eclipse 向導

實際中,我們經常遇到的一類問題是:某個向導頁的存在與否依賴於用戶在前面向導頁中所做的選擇 。

舉個例子:我們需要給某電影院實現一個電影票購票系統,觀眾能夠通過該系統遠程連接到電影院的 服務器進行訂票,訂購電影票的用戶有三種類型:普通用戶,會員用戶,以及 VIP 會員,不同的用戶其 訂票流程是不同的。根據需求,我們使用 Eclipse 的 RCP 來實現該電影票購票系統,觀眾訂票操作將通 過 Eclipse 向導輔助完成,我們設計了四個向導頁:A、B、C、D。向導頁 A 供用戶選擇其用戶類型:普 通用戶,普通會員或者 VIP 會員。假如觀眾是普通用戶則直接進入向導頁 B 填寫其基本信息,包括姓名 ,電話號碼等等,之後進入向導頁 C 選擇電影票,包括電影名字,時間,座位號等,然後選擇確定,操 作結束。假如觀眾是會員或者 VIP 會員時,觀眾在向導頁 A 輸入自己的會員 ID 和密碼登錄到系統,登 錄成功之後,直接進入向導頁 C 選擇電影票,操作和普通用戶一樣,當電影票選票結束之後,會員還將 進入向導頁 D,查看其歷史記錄,剩余余額等信息。四個向導頁的具體描述如下:

表 1. 電影訂票向導設計

向導頁編號 標題 內容 A 用戶類型選擇 選擇用戶類型:普通用戶,普通會員,VIP 會員。如果是會員用戶,需要輸入自己的會員 ID 和密碼進行登錄 B 普通用戶基本信息 對於普通用戶,填寫其基本信息,包括用戶名,電話號碼 C 電影票信息 用戶選擇其想看的電影,選擇電影名字,放映時間,以及選擇座位號 D 歷史記錄 對於會員用戶,操作結束之後,將顯示其歷史紀錄

向導頁順序圖如下,向導頁是動態變化的。當用戶為普通用戶與,向導頁 B 出現,D 不出現,當用戶 為會員用戶,向導頁 B 不出現,D 出現。

圖 2. 電影訂票動態向導順序圖

對於普通用戶:在向導頁 A 中選擇普通用戶類型,然後進入向導頁 B,填寫其基本信息,然後進入向 導頁 C 進行選票並結束。 見下圖。

圖 3. 普通用戶電影訂票流程

對於會員或者 VIP 會員用戶:在向導頁 A 中選擇會員用戶或者 VIP 會員,跳過向導頁 B,直接進入 向導頁 C,選票結束之後,進入向導頁 D,查看其歷史紀錄。見下圖。

圖 4. 會員用戶以及 VIP 會員 用戶電影訂票流程

為了實現向導頁的動態增加或者減少功能,本文提供了一個動態向導的抽象類 DynamicPageWizard, 供廣大開發人員使用。用戶使用時,只需要繼承該抽象類,並重寫向導頁初始化,增加,修改,以及刪除 等方法,便可以方便地實現動態向導。該向導類繼承了類 org.eclipse.jface.wizard.Wizard,在原 Wizard 的基礎上進行擴展,使用變量 List<IWizardPage> pages 來存儲向導中的所有向導頁,類 中所有對向導頁的操作都是基於該變量進行,包括向導頁的創建、注銷、動態增加以及刪除。詳細的內容 見下面清單。由於使用 pages 變量覆蓋了原 org.eclipse.jface.wizard.Wizard 中的 pages 變量,因 而父類方法中基於該變量的所有方法都需要重寫,在下表中省略號部分是重寫的代碼,讀者可以根據自己 的 JDK 版本以及擴展需要,將內容補上。

清單 1. 動態頁數向導父類 DynamicPageWizard

public abstract class DynamicPageWizard extends Wizard {
   /**
    * 向導中的所有向導頁,注意:指的是增加或者減少之後的向導頁
    */
   private List<IWizardPage> pages = new ArrayList<IWizardPage> ();

   ...

   /**
    * 構造函數,創建一個空的向導
    */
   protected DynamicPageWizard() {
     super();
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#addPage (org.eclipse.jface.wizard.IWizardPage)
    */
   public void addPage(IWizardPage page) {
    // 重寫父類方法,添加向導頁,並將向導頁的向導設置為當前對象
    ...
   }

   /**
    * 在指定的向導頁前插入向導頁
    *
    * @param page
    * @param nextPage
    * @return
    */
   public boolean addPage(IWizardPage page, IWizardPage nextPage) {
    for(int i = 0; i < pages.size(); i++) {
     if(pages.get(i) == nextPage) {
     return addPage(page, i);
     }
    }
    return false;
   }

   /**
    * 在指定的位置插入向導頁
    *
    * @param page
    * @param location
    */
   public boolean addPage(IWizardPage page, int location) {
    // Invalid location
    if(location < 0 || location > pages.size())
     return false;

    // Create the new page list
    List<IWizardPage> newPages = new ArrayList<IWizardPage>();
    for(int i = 0; i < location; i++) {
     newPages.add(pages.get(i));
    }

    page.setWizard(this);
    newPages.add(page);

    for(int i = location; i < pages.size(); i++) {
     newPages.add(pages.get(i));
    }

    // Set the relationship
    if(location != pages.size())
     ((IWizardPage)newPages.get(location + 1)).setPreviousPage(page);

    ((IWizardPage)page).setPreviousPage((IWizardPage)newPages.get(location - 1));
    pages = newPages;
    return true;
   }


   /**
    * 刪除指定位置的向導頁
    *
    * @param number
    */
   public void removePage(int number) {
    if(number < 0)
     return;
    if(number > pages.size() - 1)
     return;

    if(number == 0)
     pages.remove(0);
    else if(number == pages.size() - 1)
     pages.remove(number);
    else {
     IWizardPage wizarPage = (IWizardPage)pages.get(number + 1);
     wizarPage.setPreviousPage((IWizardPage)pages.get(number - 1));
     pages.remove(number);
    }
   }

   /**
    * 刪除指定的向導頁
    *
    * @param page
    */
   public void removePage(IWizardPage page) {
    int number = -1;
    for(int i = 0; i < pages.size(); i++) {
     if(pages.get(i) == page)
     number = i;
    }

    removePage(number);
   }


   /**
    * 刪除向導中某種類名的所有向導頁
    *
    * @param number
    */
   public void removePage(String className) {
     for(int i = 0; i < pages.size(); i++) {
    if(pages.get(i).getClass().getCanonicalName().equalsIgnoreCase(className))
     removePage(i);
    }
   }

  /*
  * (non-Javadoc)
  * @see org.eclipse.jface.wizard.Wizard#addPages()
  */
   public void addPages() {
    // 重寫父類方法
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#canFinish()
    */
   public boolean canFinish() {
    // 重寫父類方法,檢測是否所有向導頁的設置都結束
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#createPageControls
    * (org.eclipse.swt.widgets.Composite)
    */
   public void createPageControls(Composite pageContainer) {
    // 重寫父類方法
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#dispose()
    */
   public void dispose() {
    // 重寫父類方法
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#getDefaultPageImage()
    */
   public Image getDefaultPageImage() {
    // 重寫父類方法
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#getNextPage
    * (org.eclipse.jface.wizard.IWizardPage)
    */
   public IWizardPage getNextPage(IWizardPage page) {
    // 重寫父類方法,獲取下一個向導頁
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#getPage(java.lang.String)
    */
   public IWizardPage getPage(String name) {
    // 重寫父類方法,獲取指定名字的向導頁
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#getPageCount()
    */
   public int getPageCount() {
    // 重寫父類方法
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#getPages()
    */
   public IWizardPage[] getPages() {
    // 重寫父類方法
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#getPreviousPage
    * (org.eclipse.jface.wizard.IWizardPage)
    */
   public IWizardPage getPreviousPage(IWizardPage page) {
    // 重寫父類方法,獲取某個向導頁之前的向導頁
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#getStartingPage()
    */
   public IWizardPage getStartingPage() {
    // 重寫父類方法,獲取起始向導頁
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#performCancel()
    */
   public boolean performCancel() {
    // 重寫父類方法
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#needsPreviousAndNextButtons()
    */
   public boolean needsPreviousAndNextButtons() {
    // 重寫父類方法
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#setForcePreviousAndNextButtons(boolean)
    */
   public void setForcePreviousAndNextButtons(boolean b) {
    // 重寫父類方法
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#performFinish()
    */
   public abstract boolean performFinish();

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#setDefaultPageImageDescriptor
    * (org.eclipse.jface.resource.ImageDescriptor)
    */
   public void setDefaultPageImageDescriptor(ImageDescriptor imageDescriptor) {
    // 重寫父類方法
   }
  }

樹狀拓撲結構的 Eclipse 向導

繼續以電影票購票系統為例說明,現在用戶對該系統提出了新的需求,要求除了預定電影票之外,訂 票者還能通過該系統申請成為會員,以及普通會員申請成為 VIP 會員。這樣用戶的操作出現了分支,我 們需要在前面實現的向導基礎上增加新的一個向導頁,以供用戶選擇操作類型:電影票預訂,申請成為會 員或者升級為 VIP 會員。假如用戶選擇“電影票預訂”,便進入上一節中實現的訂票界面。假如用戶選 擇“申請成為會員”或者“升級為 VIP 會員”,便分別進入另兩條分支,進行相應操作。

根據上面分析,我們需要實現如下向導頁。

向導頁編號 標題 內容 A 操作類型選擇 選擇用戶操作類型:電影票預訂,申請成為會員或者升級為 VIP 會員 B 用戶類型選擇 選擇用戶類型:普通用戶,普通會員,VIP 會員 C 申請成為會員 普通用戶申請成為會員操作 D 升級為 VIP 會員 普通會員升級成為 VIP 會員操作 E 普通用戶基本信息 對於普通用戶,填寫其基本信息,包括用戶名,電話號碼 F 電影票信息 用戶選擇其想看的電影,選擇電影名字,放映時間,以及選擇座位號 G 歷史記錄 對於會員用戶,操作結束之後,將顯示其歷史紀錄

該向導的樹型拓撲結構如下。需要特別指出的是,電影票預訂操作,也就是第二級節點向導頁 B 處的 實現方式和前面是完全不同的,這裡使用的是數狀的拓撲結構,而前面采用的是頁面動態增加或者減少方 式。

圖 5. 電影訂票樹狀拓撲結構

首先在向導頁 A 中選擇操作類型,如果是電影票預訂操作,則進入向導頁 B,如果是申請成為會員, 則進入向導頁 C,如果是升級為 VIP 會員,則進入向導頁 D,這是第一次分支;當用戶選擇電影票預訂 並進入向導頁 B 之後,用戶再選擇其用戶類型,這是該向導的第二次分支,如果用戶是普通用戶,則進 入向導頁 E 填寫普通用戶基本信息,然後進入向導頁 F 進行選票並結束;如果用戶是會員或者 VIP 會 員,則直接進入向導頁 F,選票結束之後,進入向導頁 G,查看其歷史紀錄。

普通用戶預定電影票操作:首先在向導頁 A 中,選擇電影票預訂,進入訂票分支;然後在向導頁 B 中,選擇普通用戶,進入普通用戶訂票分支。見下圖。

圖 6. 普通用戶電影訂票流程

會員用戶預定電影票操作:首先在向導頁 A 中,選擇電影票預訂,進入訂票分支;然後在向導頁 B 中,選擇會員用戶,輸入會員用戶名和密碼,進入會員用戶訂票分支。見下圖。

圖 7. 會員用戶電影 訂票流程

申請成為會員操作:在向導頁 A 中,選擇申請成為會員,進入申請成為會員分支。見下圖。

圖 8. 申請成為會員流程

為了實現樹狀拓撲結構,文章提供了一個樹狀向導的抽象類 TreePageWizard,以及向導頁類 TreeWizardPage 供開發人員使用。用戶使用時,只需要將其向導類繼承 TreePageWizard,向導頁類繼承 TreeWizardPage,重寫這兩個類中相應的方法,便可以方便地實現樹狀拓撲結構向導。

向導類 TreePageWizard 繼承自類 org.eclipse.jface.wizard.Wizard,在原 Wizard 的基礎上進行 擴展,使用變量 TreeWizardPage rootPage 來存儲樹結構的根節點對應的向導頁,樹結構的根節點即是 該向導的起始頁。使用變量 Set<TreeWizardPage> pages 來存儲樹結構中的所有向導頁。類中所 有操作都是圍繞這兩個變量進行,包括樹結構中節點的添加、刪除、樹結構分支的選擇等等。和動態向導 不同的是,pages 裡面保存的向導頁是向導中所有可能的頁面,而 DynamicPageWizard 中存儲的是當前 向導中的頁面,並且 pages 的類型是 Set<TreeWizardPage>,DynamicPageWizard 中類型是 List<IWizardPage>,詳細的內容見下面清單。同樣,由於 pages 變量覆蓋了原 org.eclipse.jface.wizard.Wizard 中的 pages 變量,之前基於該變量的所有方法都需要重寫,讀者可 以根據不同的 JDK 版本以及自己的需要,將必須重寫部分內容補上。

清單 2. 樹狀拓撲向導父類 TreePageWizard

public abstract class TreePageWizard extends Wizard {
   /**
    * 樹型向導的起始頁
    * Root page.
    */
   private TreeWizardPage rootPage = null;

   /**
    * 向導中的所有向導頁
    * All the pages in the wizard.
    */
   private Set <TreeWizardPage> pages = new HashSet <TreeWizardPage> ();

   /**
    * 構造函數
    * Creates a new empty wizard.
    */
   protected TreePageWizard () {
     super();
   }

   /**
    * 構造函數
    * Creates a new empty wizard.
    */
   protected TreePageWizard (IWizardPage page) {
     super();
    if(page instanceof TreeWizardPage) {
     rootPage = (TreeWizardPage)page;
     addPage(rootPage);
    }
   }

   /**
    * 設置樹結構的根節點,以及向導的起始頁
    */
   public void setRootPage(IWizardPage page) {
    if(page instanceof TreeWizardPage) {
     addPage((TreeWizardPage)page);
     rootPage = (TreeWizardPage)page;
    }
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#addPage (org.eclipse.jface.wizard.IWizardPage)
    */
   public void addPage(IWizardPage page) {
    // 判斷是否 TreeWizardPage 類型的向導,如果不是,則返回
    if(!(page instanceof TreeWizardPage))
     return;

    // 添加該向導頁
    pages.add((TreeWizardPage)page);
    page.setWizard(this);

    // 添加該向導頁的所有子向導頁
    List <TreeWizardPage> children = ((TreeWizardPage)page).getChildren ();
    for(int i = 0; i < children.size(); i++) {
     addPage(children.get(i));
    }
   }

   /**
    * 插入向導頁,第一個參數為待插入的向導頁,第二個參數為插入位置之前的向導頁
    *
    * @param page
    * @param prviousPage
    * @return
    */
   public void addPage(TreeWizardPage page, TreeWizardPage previousPage) {
    boolean exist = pages.contains(previousPage);
    if(!exist)
     return;

    if (previousPage.getChildren().add(page))
     addPage(page);
   }

   /**
    * 從向導中刪除向導頁
    *
    * @param page
    * @return
    */
   public void removePage(TreeWizardPage page) {
    pages.remove(page);
    List <TreeWizardPage> children = page.getChildren();
    for(int i = 0; i < children.size(); i++) {
     removePage(children.get(i));
    }
   }

   /**
    * 從向導中刪除向導頁,第一個參數為待刪除的向導頁,第二個參數為刪除位置之前的向導頁
    *
    * @param page
    * @param prviousPage
    * @return
    */
   public void removePage(TreeWizardPage page, TreeWizardPage previousPage) {
    boolean exist = pages.contains(previousPage);
    if(exist) {
     if (previousPage.getChildren().remove(page))
     removePage(page);
    }
   }

   /**
    * 從向導中刪除向導頁,第一個參數為待刪除的向導頁的類名,第二個參數為刪除位置之前的 向導頁
    *
    * @param className
    * @param previousPage
    * @return
    */
   public boolean removePage(String className, TreeWizardPage previousPage) {
    boolean exist = pages.contains(previousPage);
    if(!exist)
     return false;

    List <TreeWizardPage> children = previousPage.getChildren();
    for(int i = 0; i < children.size(); i++) {
     TreeWizardPage temp = children.get(i);
     if(temp.getClass().getCanonicalName().equalsIgnoreCase(className)) {
     if (children.remove(temp)) {
      removePage(temp);
       return true;
       } else
       return false;
     }
    }

    return false;
   }

  /*
  * (non-Javadoc)
  * @see org.eclipse.jface.wizard.Wizard#addPages()
  */
   public void addPages() {
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#canFinish()
    */
   public boolean canFinish() {
    // 檢測是否所有向導頁的設置都完成,整個向導可以結束
    TreeWizardPage page = rootPage;
    while (page != null) {
     if(!page.isPageComplete())
     return false;
     page = page.getNextPage();
    }

     return true;
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#createPageControls
    * (org.eclipse.swt.widgets.Composite)
    */
   public void createPageControls(Composite pageContainer) {
    // 重寫父類方法
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#dispose()
    */
   public void dispose() {
    // 重寫父類方法
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#getDefaultPageImage()
    */
   public Image getDefaultPageImage() {
    // 重寫父類方法
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#getPreviousPage
    * (org.eclipse.jface.wizard.IWizardPage)
    */
   public IWizardPage getPreviousPage(IWizardPage page) {
    // 獲取特定向導頁之前的頁面 
     if(page == null)
      return null;

     if(!(page instanceof TreeWizardPage))
      return null;

     return ((TreeWizardPage)page).getPreviousPage();
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#getNextPage
    * (org.eclipse.jface.wizard.IWizardPage)
    */
   public IWizardPage getNextPage(IWizardPage page) {
    // 獲取特定向導頁之後的頁面 
    if(page == null)
     return null;

    if(!(page instanceof TreeWizardPage))
     return null;

    return page.getNextPage();
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#getPage(java.lang.String)
    */
   public IWizardPage getPage(String name) {
    // 根據名字,獲取特定向導頁
    Iterator <TreeWizardPage> iter = pages.iterator();
    while(iter.hasNext()) {
       IWizardPage page = (IWizardPage) iter.next();
       String pageName = page.getName();
       if (pageName.equals(name)) {
    return page;
   }
     }
     return null;
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#getPageCount()
    */
   public int getPageCount() {
    // 重寫父類方法,獲取該樹型結構向導中所有的向導頁的數目
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#getPages()
    */
   public IWizardPage[] getPages() {
    // 重寫父類方法,獲取該樹型結構向導中所有的向導頁
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#getStartingPage()
    */
   public IWizardPage getStartingPage() {
    // 重寫父類方法,獲取起始向導頁
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#performCancel()
    */
   public boolean performCancel() {
    // 重寫父類方法
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#needsPreviousAndNextButtons()
    */
   public boolean needsPreviousAndNextButtons() {
    // 重寫父類方法
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#setForcePreviousAndNextButtons(boolean)
    */
   public void setForcePreviousAndNextButtons(boolean b) {
    // 重寫父類方法
    ...
   }

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#performFinish()
    */
   public abstract boolean performFinish();

   /*
    * (non-Javadoc)
    * @see org.eclipse.jface.wizard.Wizard#setDefaultPageImageDescriptor
    * (org.eclipse.jface.resource.ImageDescriptor)
    */
   public void setDefaultPageImageDescriptor(ImageDescriptor imageDescriptor) {
     // 重寫父類方法
     ...
   }
  }

我們再來看一下向導頁類 TreeWizardPage 的實現,該類繼承了類 org.eclipse.jface.wizard.WizardPage,樹狀向導中所有的向導頁都必須繼承此類,使用變量 List <TreeWizardPage> children 存儲該樹節點所有的孩子節點,使用變量 TreeWizardPage nextPage 存儲當前向導頁將要跳轉到的下一向導頁,nextPage 必須是 children 中的某一個向導頁。 TreeWizardPage 類中其他方法實現了對該向導頁所有孩子節點的增加、刪除、獲取等操作,以及設置下 一向導頁等操作,完整內容見下面清單。

清單 3. 樹狀拓撲向導頁父類 TreeWizardPage

public class TreeWizardPage extends WizardPage {
   /**
   * 當前向導頁所有可能的下一個向導頁
   */
   private List <TreeWizardPage> children = new ArrayList  <TreeWizardPage>();

   /**
   * 當前向導的下一個向導頁
   */
   private TreeWizardPage nextPage = null;

   /**
   * 構造函數
   *
   * @param pageName
   */
   protected TreeWizardPage(String pageName) {
    super(pageName);
   }

   /**
   * 構造函數
   *
   * @param pageName
   * @param title
   * @param titleImage
   */
   protected TreeWizardPage(String pageName, String title,
      ImageDescriptor titleImage) {
    super(pageName, title, titleImage);
   }

   /*
   * (non-Javadoc)
   *
   * @see
   * org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets
   * .Composite)
   */
   public void createControl(Composite parent) {
   }

   /*
   * (non-Javadoc)
   *
   * @see org.eclipse.jface.wizard.WizardPage#getNextPage()
   */
   public TreeWizardPage getNextPage() {
    return nextPage;
   }

   /**
   * 設置該向導的下一頁
   *
   * @param nextPage
   *      the nextPage to set
   */
   public void setNextPage(TreeWizardPage nextPage) {
    for (int i = 0; i < children.size(); i++) {
     if (children.get(i) == nextPage) {
      this.nextPage = nextPage;
      updateContainerButton();
     }
    }

    this.nextPage = null;
   }

   /**
   * 設置該向導的下一頁
   *
   * @param className
   */
   public void setNextPage(String className) {
   int number = -1;
    for (int i = 0; i < children.size(); i++) {
     if (children.get(i).getClass().getCanonicalName().equalsIgnoreCase(className))
      number = i;
    }
    if (number != -1) {
     this.nextPage = children.get(number);
     updateContainerButton();
    } else
     this.nextPage = null;
   }

   /**
   * 添加可能的下一個向導頁
   *
   * @param page
   */
   public void addChild(TreeWizardPage page) {
    if (page != null) {
     children.add(page);
     page.setPreviousPage(this);
    }
   }

   /**
   * 刪除可能的下一個向導頁
   *
   * @param page
   */
   public void removeChild(TreeWizardPage page) {
    if (page != null) {
     if (children.contains(page))
      children.remove(page);
    }
   }

   /**
   * 獲取所有可能的下一個向導頁
   *
   * @return the children
   */
   public List <TreeWizardPage> getChildren() {
    return children;
   }

   /**
   * 更新向導中的按鈕,包括上一步按鈕,下一步按鈕,結束按鈕以及取消按鈕 
   */
   public void updateContainerButton() {
    getContainer().updateButtons();
   }
  }

關於上文中兩個例子的具體實現,讀者可以在附件 EclipseWizardExtension.zip 中找到,考慮到不 同 JDK 以及版權的問題,並沒有將兩種向導的父類 DynamicPageWizard 和 TreePageWizard 放在附件中 ,讀者僅需要在 org.eclipse.expand.wizard 包中重建這兩個類,並把文中省略部分補全即可。

小結

本文對 Eclipse 向導機制進行了兩個方面的擴展,在原有 Eclipse 向導的基礎上,擴展實現了一種 動態頁數的向導,以及一種具有樹狀拓撲結構的向導。動態頁數向導支持在向導中任意添加或者刪除新的 向導頁,實現時向導類需要繼承 DynamicPageWizard 類,並重寫動態添加或者刪除向導頁方法;而樹狀 拓撲結構使得向導根據用戶不同選擇,其流程可以實現分支,樹狀向導需要繼承 TreePageWizard 類,向 導頁需要繼承 TreeWizardPage 類,並分別重寫父類中的相應方法。基於這兩種擴展,用戶可以快速編寫 復雜的 Eclipse 向導實現,滿足實際需要。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved