Eclipse 向导机制扩展 -- 实现可定制的向导
2010-05-31 00:00:00 来源:WEB开发网本文首先剖析了 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 向导实现,满足实际需要。
- ››Eclipse+SVN+Google Code配置过程
- ››eclipse中开发android程序时,打开layout配置文件自...
- ››Eclipse快捷键大全
- ››Eclipse Helios 之旅:看看 Eclipse 的最新同步发...
- ››Eclipse和MyEclipse的关系
- ››Eclipse 环境下的 OpenSocial 开发:通过 Shindig...
- ››Eclipse 向导机制扩展 -- 实现可定制的向导
- ››Eclipse 中的 EJB V3.0 数据库持久化
- ››Eclipse 常用快捷键
- ››Eclipse 插件开发 -- 深入理解菜单(Menu)功能...
- ››Eclipse 插件开发-如何扩展 WTP Wizard
- ››Eclipse Android 开发环境 搭建
赞助商链接