一、Eclipse简介
Eclipse最初是由IBM捐献给开源社区的,目前已经发展成为人气最旺的Java IDE。Eclipse插件化的功能模块吸引了无数开发者开发基于Eclipse的功能插件。事实上,Eclipse已经超越了一般Java IDE的概念。Eclipse是一个平台,一个开放的平台,你可以为Eclipse添加任何你想要的功能,比如播放音乐,观看电影,聊天……这些不是天方夜谭,而是已经实现的事实。虽然Eclipse可以添加很多附加功能,可以编辑C/C++,可以编辑Word文件,可以开发UML等等,但是Eclipse最基本,也是最强大的功能还是Java IDE。
二、RCP简介
RCP的全称是Rich Client Platform,可以把它看成是Eclipse的骨架,其他的插件是器官与血肉。我们可以把这个骨架拿过来填入自己的器官和血肉,这样就创造了我们自己的“Eclipse”!
使用RCP来开发Java桌面应用可以把开发的焦点转移到系统的逻辑功能上,而不是界面上。我们自己的程序可以继承Eclipse的风格与功能,而不用自己去编写诸如菜单,工具条,子窗口等等的界面元素。甚至我们可以把Eclipse本身的功能插件,比如Console拿来放在自己的程序里,这样就避免了很多重复开发。
三、知识准备
我写这篇文章并不是面向Java的初学者,而是面向有一定Eclipse使用基础的开发者。所以我假设你已经具备一下基本知识:
1、 Java基础
2、 用过Eclipse进行开发
3、 SWT/JFace开发基础(可选)
如果你还不具备上述条件,那么看我的文章你会看的很郁闷,建议你先去学习这些基本知识。
四、Eclipse组件
在开发Eclipse插件(RCP可以看成是Eclipse的插件,只不过是脱离Eclipse运行的)之前,得先对Eclipse的结构有个了解。这里我简单介绍一下Eclipse的基本组件,这些名词可能比较陌生,但这都是开发Eclipse插件必须了解的。
如上图所示,我逐一介绍一个各个组件:
1、 Menu bar:这个东西你一定不陌生,每个软件都有的。不过Eclipse的菜单栏是动态的,也就是说,根据所编辑的内容不同,显示的菜单也可以不一样。
2、 Tool bar:这个东西也是每个软件都有的,和菜单栏一样,工具栏也是可以根据所编辑的内容不同而不同。
3、 Editor:编辑器,Eclipse的主要编辑工作是在Editor里面完成的。
4、 View:视图,视图是为了方便用户编辑提供一些辅助功能或编辑一些属性。比如最常见的Outline视图往往用来提供当前编辑的文档的结构。
5、 Page:页,一个页表示了当前用户的工作状态,包括View和Editor。
6、 Workbench Window:涵盖所有上述组件的组件叫做工作台窗口(这个名词的翻译我没见到过,我这里纯粹是直译,感觉有些词不达意)。Eclipse是允许创建多个工作台的。通过Window->New window菜单可以创建当前工作台的副本。
除了这些组件以外我还要介绍另外两个概念,一个是“Work Space”,在Eclipse启动的时候都要求指定一个Work Space,而且Work Space是不能被共用的。也就是说在同一时间,同一个Work Space只可以被一个Eclipse使用。但是一个Work Space是可以被多个Workbench Window共享的。很容易联想到,Workbench Window上面还有一层Workbench。事实上Workbench才是Eclipse的UI的最高管理者。另外一个概念是“Perspective”,中文翻译是“透视图(或者观察点)”。所谓Perspective是指当前Page的布局。最常见的是Java透视图和Debug透视图,可以看到这两个透视图的Page排布完全不一样。通过切换透视图可以很方便的切换开发环境以完成不同功能的开发。这里可以看出View和Editor的区别,Editor是在不同的透视图中共享的,而View不是。
五、开发前的准备
Eclipse是自带插件开发环境PDE(Plug-in Develop Environment)的,所以要开发Eclipse插件只需要下载一个标准的Eclipse即可。我现在用的Eclipse版本是3.3.0,是最新的稳定版,建议下载这个版本进行开发(我用的是英文版,所以下文提到的Eclipse相关的选项都是英文描述)。
虽然Eclipse生来就是开放的插件平台,但是Eclipse插件,特别是RCP是从3.0开始才走红的。Eclipse 3.0是一个具有里程碑意义的版本,它对Eclipse以前的结构做了一定的改进,并且升级了PDE,极大的简化了插件开发的配置,基本上实现了插件开发全图形的化操作,使得插件开发人员可以专注于插件功能的开发,而不用去管琐碎的配置文件。
六、第一个RCP程序
Eclipse提供了一些RCP程序的模板,通过PDE的插件创建向导能直接生成一个可用的RCP程序。
首先要新建一个Plug-in Project
然后输入Project名字,其他都用默认选项就行,点击“next”
在Rich Client Application部分选择“Yes”,点击“Next”
模板选择Hello RCP,点击“Next”
点击“Finish”将提示转换到Plug-in development perspective,选择“Yes”。
我们先看一下刚才新建的RCP的运行结果,右键点击“Demo”project,选择Run As,选择Eclipse Application,就可以看到一个最简单的RCP程序窗口。
接下来我先分析一下这个应用程序的结构。在Demo包下面有六个类和三个配置文件,下面我解释一下这些元素:
六个类
三个配置文件
1、 Application:这个类是程序的入口,虽然没有Main函数,但是这个类实现了IPlatformRunnable接口,当JVM完毕,初始化RCP框架以后会调用这个类的run函数来完成UI设置和开始执行我们指定的程序功能。在绝大多数RCP程序中,这个类不用更改。
2、 ApplicationActionBarAdvisor:简单的说这个类是用来配置程序的菜单栏和工具栏的,具体的应用在后面会讲到。
3、 ApplicationWorkbenchAdvisor:这个类是RCP程序的Workbench,RCP是Eclipse的简化,但是所有的组件都是和Eclipse一样的。一个RCP程序也只能有一个Workbench。
4、 ApplicationWorkbenchWindowAdvisor:这个类是RCP的WorkbenchWindow,隶属于当前的Workbench。可以有多个WorkbenchWindow。
5、 DemoPlugin:这个类代表了我们的插件,因为RCP程序也是一个插件,Eclipse中所有的插件都必须继承AbstractUIPlugin。这个类为我们提供了很多和插件相关的信息,比如插件的资源,配置等等。
6、 Perspective:是我们新建的RCP的默认透视图。可以在这个类中指定View和Editor的排布。
7、 plugin.xml:这个文件是我们插件的配置文件,包括我们的插件用了哪些其他的插件,具体是怎么配置这些插件的等等。
8、 build.properties:这个文件是用来配置我们插件的编译信息的,用来指定如何编译我们的插件。
9、 MANIFEST.MF:这个文件用来指定我们插件的元数据,比如插件的版本信息。一般来说,这个文件不用手动的去更改。
七、添加菜单和工具栏
这一节我演示以下如何为程序创建菜单和工具栏。在创建菜单和工具栏之前我想先介绍一个概念“Action”。开发Eclipse插件会经常看到这个东西,它和事件处理有点相似,如果你了解JFace的话,这里的Action你应该很熟悉,两者是一样的,我们自己定义的Action必须继承org.eclipse.jface.action.Action。程序可以定义当用户执行某项操作时触发某个Action。比如用户点击工具栏的一个按钮,或者选中了某个菜单项。
下面我们创建一个类HelloAction
package firstrcp;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.internal.IWorkbenchGraphicConstants;
import org.eclipse.ui.internal.WorkbenchImages;
public class HelloAction extends Action {
private IWorkbenchWindow window;
public HelloAction(IWorkbenchWindow window) {
this.window = window;
this.setText("Hello");
ImageDescriptor imgDes = WorkbenchImages.getImageDescriptor(IWorkbenchGraphicConstants.IMG_ETOOL_HOME_NAV);
this.setImageDescriptor(imgDes);
}
}
由于Action是一个UI元素,所以往往创建一个带IWorkbenchWindow参数的构造函数,以便在Action内部调用。setText()是设置Action对外显示的名字。setImageDescriptor()是设置Action的图标,这里我为了简化,用的是Eclipse Workbench自带的图标。
下面增加一个run方法,功能是弹出一个对话框显示“Hello World!”
public void run() {
MessageBox mb = new MessageBox(window.getShell(), SWT.OK);
mb.setMessage("Hello world!");
mb.setText("Demo");
mb.open();
}
这里你可以看到构造函数传入window的好处了吧。
上述代码都很简单,我不多解释,接下来我们把创建的Action关联到菜单栏和工具栏上。我们可以把同一个Action同时关联到多个菜单项和工具栏按钮,这和事件处理中的事件处理函数可以被多个事件共享一样。
上文我已经提到过,ApplicationActionBarAdvisor是用来配置工具栏和菜单栏的,那么我们现在来看以下具体如何操作。修改ApplicationActionBarAdvisor如下:
package firstrcp;
import org.eclipse.jface.action.ICoolBarManager;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.ToolBarContributionItem;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.swt.SWT;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.application.ActionBarAdvisor;
import org.eclipse.ui.application.IActionBarConfigurer;
public class ApplicationActionBarAdvisor extends ActionBarAdvisor {
private HelloAction helloAction;
public ApplicationActionBarAdvisor(IActionBarConfigurer configurer) {
super(configurer);
}
//用来初始化action
protected void makeActions(IWorkbenchWindow window) {
super.makeActions(window);
helloAction = new HelloAction(window);
}
/* 用来设置菜单栏.创建一个MenuManager,然后把helloAction加入到这个菜单下面,
* 如果有多个action的话,可以加入同一个MenuManager,
* 这样demoMenu相当于一个下拉菜单,
* 而加入的action就是菜单项。最后把demoMenu加入到菜单栏。
*/
protected void fillMenuBar(IMenuManager menuBar) {
super.fillMenuBar(menuBar);
MenuManager demoMenu = new MenuManager("&Demo", "");
demoMenu.add(helloAction);
menuBar.add(demoMenu);
}
/*
* 用来设置工具栏的,原理和菜单栏是一样的,coolBar也有分组的,
* 每个toolbar就是一组。这里的toolbar相当于demoMenu。
*/
protected void fillCoolBar(ICoolBarManager coolBar) {
IToolBarManager toolbar = new ToolBarManager(SWT.FLAT | SWT.RIGHT);
coolBar.add(new ToolBarContributionItem(toolbar, "main"));
toolbar.add(helloAction);
}
}
代码看起来有点晕,我来一一解释。首先是makeActions()方法,这个方法是用来初始化action的,所有要用到的action可以都在这里初始化,但是这一步与界面无关。fillMenuBar()是用来设置菜单栏的。先创建一个MenuManager,然后把helloAction加入到这个菜单下面,如果有多个action的话,可以加入同一个MenuManager,这样demoMenu相当于一个下拉菜单,而加入的action就是菜单项。最后把demoMenu加入到菜单栏。当然,一个菜单栏可以加入多个下拉菜单。多级菜单也是可以做的,有兴趣的可以自行研究。fillCoolBar()是用来设置工具栏的,原理和菜单栏是一样的,coolBar也有分组的,每个toolbar就是一组。这里的toolbar相当于demoMenu。
运行一下程序你会发现菜单栏出来了,但是工具栏没有出现。原因是Hello RCP默认是不显示工具栏的,我们得改一下设置。打开ApplicationWorkbenchWindowAdvisor类,更改preWindowOpen方法:
public void preWindowOpen() {
IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
configurer.setInitialSize(new Point(400, 300));
//configurer.setShowCoolBar(false);
configurer.setShowStatusLine(false);
configurer.setTitle("Hello RCP");
}
以上代码也很简单,从字面就能看出什么意思,我也不多解释,注意看注释掉的那行。preWindowOpen()这个函数在程序的窗口打开之前被调用,可以在这个函数里设置一些初始化的内容。类似的还有postWindowClose()等等,你可以查阅API来了解这些函数的用途。
现在我们的程序可以正确的运行了,看一下运行结果:
八、创建View和Editor
我用Designer来编辑View和Editor。我用的版本是WindowBuilder Pro 4.1.0,建议你也去下载一个,因为这个插件能够支持SWT,JFace,RCP,Swing的可视化编辑,极大的缩短了界面开发的时间。不过这个是商业软件,要破解了才能用,网上有很多破解的方法,google一下就行了。
下面开始创建第一个View。
右键点击demo包,选择“New”,“Other” 然后选择Designer->RCP->ViewPart,点击“Next”
输入“HelloView”,点击“Finish”
你可以在Source和Design之间切换。View相当于一个窗口,在View的界面上可以放任何组件。我拖了一个button上去,名字就叫button。HelloView类中要添加一个属性ID,我稍后解释这个属性。
你可以用同样的方法创建一个EditorPart,名字就叫HelloEditor。在editor上面放一个text组件吧,这样看起来像个editor,然后把HelloEditor的layout改成FillLayout。当然也要为HelloEditor添加一个ID属性。这里需要改一下Editor的init方法,否则editor无法正常运行。
setSite是让editor能够使用workbench所提供的一些功能,EditorSite可以看成是一个代理,EditorPart通过EditorSite来访问workbench。EditorSite是editor part和workbench之间的一层接口,详细情况你可以去google一下或者参考API。setInput的意思就很明了,就是让editor知道显示什么东西。这样我们就创建好了基本的View和Editor,下面来看怎么把它们加到我们的程序中去。
打开plugin.xml,选择Extensions标签页,在All Extensions部分点击“Add”按钮,选择org.eclipse.ui.views, org.eclipse.ui.editors, 两个extension,结果如下:
右键org.eclipse.ui.view,选择New->view
在Extension Element Details部分设置View的属性。ID可以随意设置,但是不能重复,我这里设置为Demo.helloView(虽说是随意设置,但是程序是通过这个ID来获得View的,所以最好与HelloView中的ID属性一致,我在HelloView中加入这个ID属性也是为了方便程序获得HelloView),Name也可以随意设置,我这里设置为Hello View,这个属性会显示在View的顶端,就像Outline View顶部显示的名称是“Outline”。class选择刚才创建的demo.HelloView。icon是显示在View顶部的图标,没有可以不设置。
类似的创建Hello Editor,设置Hello Editor的详细属性,设置id, name, class三个属性以外,还要设置icon属性(Editor的Icon属性是必须的!),随便给个图片文件就行,其他的属性可以不填。这里属性比较多,有兴趣可以去看Eclipse的插件开发帮助,里面对每个属性都有解释。
到目前为止,我们的配置工作已经完成,但是要让我们的程序正确的显示View和Editor,我们还要更改默认的Perspective。打开Perspective.java,修改createInitialLayout()方法(默认这个方法是空的)
public
这个方法实际上就是修改layout,在这个方法中安排每个view的位置和大小。addStandaloneView()是为当前的Perspective添加一个独立的View,所谓独立的View就是View所占的位置不能和其他View共享,你在Eclipse里应该看到过几个View叠在一起的情况。接下来一行设置是去除了关闭View的功能,就是这个View的顶部没有关闭按钮。我们来看一下运行结果
void createInitialLayout(IPageLayout layout)
{
layout.addStandaloneView(HelloView.ID, true,
IPageLayout.LEFT, .50f, layout.getEditorArea());
layout.getViewLayout(HelloView.ID).setCloseable(false);
}
也许你正在想怎么打开Editor,IPageLayout并不能直接把一个Editor加入显示,但是它会预留一块空间给Editor。如果你仔细观察上面那段代码,你会发现,事实上整个空间都是Editor的,View是在瓜分Editor的空间。接下来我将介绍如何打开Editor。
九、设置并打开Editor
相对View来说,Editor有点麻烦。因为要打开Editor的话必须给Editor内容,因为Editor是个编辑器,你得让它知道要编辑什么东西它才能打开。这里的内容就是Eclipse里面的EditorInput。没有现成合适的EditorInput用(一般情况下可以用FileEditorInput,把某个文件作为Input让Editor打开,在Eclipse里面双击打开某个文件就是这个过程),我这里创建一个HelloEditorInput继承IEditorInput接口。用Eclipse向导创建类的时候,先选择Interface,然后记得选中“Inherited abstract methods”,这样会自动继承所有abstract的方法,不用再去手工创建了。
HelloEditorInput的代码比较长,我不全部贴出来了,我贴一部分。我贴的两个方法是必须要修改的,这个类的其他代码用Eclipse自动生成的就好(做实际开发的时候肯定是要改的,我这里只是演示一个过程,所以尽量简化了)。
public
这里的getName是返回当前input的名字,比如文件名,getToolTipText是返回当前input的提示,当鼠标移动到editor的顶部标签上时就会显示。这两个方法都不能返回null。
好了现在一切准备工作已经就绪,就等打开Editor了。我们通过HelloView上面的那个按钮来打开HelloEditor,每按一次按钮打开一个Editor。首先更改按钮的Text属性为“Open Editor”,然后为按钮添加事件,事件代码如下:
关键代码就一行,就是那个openEditor方法,这个函数的两个参数,前面的代表资源,后面的代表工具,就是用后面的Editor去打开前面的内容。提醒一下,这里的Editor ID必须和plugin.xml文件里面的一样,否则会找不到合适的Editor。可以看到openEditor是Page的方法,这也和我前面介绍的Eclipse的组件结构一致,View和Editor是属于某个page的。其实在配置Perspective的时候也能看出这个问题,我们都是用IPageLayout
String getName()
{
return "Hello";
}
public String getToolTipText()
{
return "Demo";
}
openEditorButton.addSelectionListener(new SelectionAdapter()
{
public void widgetSelected(SelectionEvent e)
{
try
{
getViewSite().getWorkbenchWindow().getActivePage()
.openEditor(new HelloEditorInput(), HelloEditor.ID);
}
catch (Exception ex)
{
System.out.println(ex);
}
}
});
来布置界面。下面运行一下程序,看一下运行结果。
可能你会感觉不太协调,那么我们把窗口默认最大化(似乎能好看点),这就需要修改ApplicationWorkbenchWindowAdvisor类中的postWindowOpen函数:
如果你以前接触过SWT/JFace的话这段代码应该很熟悉吧,就算没接触过,也应该能大概明白什么意思,所以我不解释了。这句话一定要放在postWindowOpen里面,因为在preWindowOpen的时候还没有Window存在,所以没法设置。我们来看一下最终的运行结果:
下面我介绍一下如何导出/发布RCP程序,使程序能够独立运行。
public void postWindowOpen()
{
this.getWindowConfigurer().getWindow().getShell().setMaximized(true);
}
十、导出RCP程序
从Eclipse 3.0开始,RCP程序的配置和导出就变得相当方便。RCP程序又称为Eclipse Product,我们先为Demo Project新建一个Product配置文件。右键Demo Project,选择New->Other->Product Configuration,然后选择Demo文件夹,File name输入demo.product,点击Finish
接下来我们要新建一个Product Id,点击New
然后点击Browse选择Demo,并输入Product ID为hello,点击Finish
Overview标签页的Product Definition部分,输入Product Name为Hello Demo。在Testing部分点击一下“Synchronize”(最好点一下,有时候不点也没问题),这是为了让我们的Product配置和插件的配置保持一致。然后切换到Configuration标签页,点击Add,输入Demo,点击OK
然后点击“Add Required Plug-ins”,这一步是添加我们的Product所用到的其他插件,这样才可以脱离Eclipse运行。
然后进入Branding标签页,这一页是设置Product的图标,程序名字等等,可以不设置,系统会自动使用默认值,我这里输入一个Launcher Name为HelloDemo。
现在我们已经完成了导出的配置工作,现在来导出我们的RCP程序。进入Demo.product的Overview标签页,在右下角Exporting部分点击“Eclipse Product export wizard”,输入Root Directory和Export Destination,点击Finish。