Eclipse向导机制扩展-实现可定制的向导

实现动态页数和树状拓扑结构的 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 pages 来存储向导中的所有向导页,类 中所有对向导页的操作都是基于该变量进行,包括向导页的创建、注销、动态增加以及删除。详细的内容 见下面清单。由于使用 pages 变量覆盖了原 org.eclipse.jface.wizard.Wizard 中的 pages 变量,因 而父类方法中基于该变量的所有方法都需要重写,在下表中省略号部分是重写的代码,读者可以根据自己 的 JDK 版本以及扩展需要,将内容补上。清单 1. 动态页数向导父类 DynamicPageWizard

public abstract class DynamicPageWizard extends Wizard {   /**    * 向导中的所有向导页,注意:指的是增加或者减少之后的向导页    */   private List pages = new ArrayList ();   ...   /**    * 构造函数,创建一个空的向导    */   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  pages.size())     return false;    // Create the new page list    List newPages = new ArrayList();    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  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 pages 来存储树结构中的所有向导页。类中所 有操作都是围绕这两个变量进行,包括树结构中节点的添加、删除、树结构分支的选择等等。和动态向导 不同的是,pages 里面保存的向导页是向导中所有可能的页面,而 DynamicPageWizard 中存储的是当前 向导中的页面,并且 pages 的类型是 Set,DynamicPageWizard 中类型是 List,详细的内容见下面清单。同样,由于 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  pages = new HashSet  ();   /**    * 构造函数    * 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  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  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  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  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 children 存储该树节点所有的孩子节点,使用变量 TreeWizardPage nextPage 存储当前向导页将要跳转到的下一向导页,nextPage 必须是 children 中的某一个向导页。 TreeWizardPage 类中其他方法实现了对该向导页所有孩子节点的增加、删除、获取等操作,以及设置下 一向导页等操作,完整内容见下面清单。

清单 3. 树状拓扑向导页父类 TreeWizardPage

public class TreeWizardPage extends WizardPage {   /**   * 当前向导页所有可能的下一个向导页   */   private List  children = new ArrayList  ();   /**   * 当前向导的下一个向导页   */   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  getChildren() {    return children;   }   /**   * 更新向导中的按钮,包括上一步按钮,下一步按钮,结束按钮以及取消按钮    */   public void updateContainerButton() {    getContainer().updateButtons();   }  }

关于上文中两个例子的具体实现,读者可以在附件 EclipseWizardExtension.zip 中找到,考虑到不 同 JDK 以及版权的问题,并没有将两种向导的父类 DynamicPageWizard 和 TreePageWizard 放在附件中 ,读者仅需要在 org.eclipse.expand.wizard 包中重建这两个类,并把文中省略部分补全即可。

小结

本文对 Eclipse 向导机制进行了两个方面的扩展,在原有 Eclipse 向导的基础上,扩展实现了一种 动态页数的向导,以及一种具有树状拓扑结构的向导。动态页数向导支持在向导中任意添加或者删除新的 向导页,实现时向导类需要继承 DynamicPageWizard 类,并重写动态添加或者删除向导页方法;而树状 拓扑结构使得向导根据用户不同选择,其流程可以实现分支,树状向导需要继承 TreePageWizard 类,向 导页需要继承 TreeWizardPage 类,并分别重写父类中的相应方法。基于这两种扩展,用户可以快速编写 复杂的 Eclipse 向导实现,满足实际需要。

与一个赏心悦目的人错肩,真真实实的活着,也就够了。

Eclipse向导机制扩展-实现可定制的向导

相关文章:

你感兴趣的文章:

标签云: