最近在客户端软件开发过程中,使用到了Eclipse
RCP的若干技术,有种相见很晚的感觉。尽管自己在日常开发中也使用了SWT中的若干技术,在运用了Theme之后看起来也让人眼前一亮,但是比起RCP
就相当于闭门造车了,例如下面两幅图片,就是SWT技术和RCP技术开发出的不同界面,后者看起来是不是很炫呢,用流行的话来讲,就是使用户的体验更
rich了,呵呵。
SWT界面:
Eclipse RCP界面:
好了,既然Eclipse RCP开发出的界面如此漂亮,那我们就可以借助Eclipse
RCP平台中各类UI组件来改善应用程序上的UI缺陷,通过RCP平台上的各类丰富的GUI视图,来打造赏心悦目的应用程序。但是在RCP开发的过程中,
UI视图之间是相互依赖的,需要进行交互,目前国内教程对这一步骤往往语焉不详,下文就以实例的形式详细介绍Eclipse RCP
View视图之间的交互机制,并总结了开发过程中的一些注意事项,避免大家绕了弯路,呵呵。
(一)下载最新的Eclipse RCP开发环境
开发RCP的首要前提是下载RCP开发环境,从http://www.eclipse.org/上
可以下载,下载之后是个zip格式文件,解压到任何目录下都可以直接运行,前提是装好jdk。在Eclipse的官方网站上,针对不同的开发环境,已经为
我们打好了不同的package,只要下载专门用来开发RCP的package即可。当然如果你已经安装了Eclipse普通开发环境,想增加RCP相关
的开发功能,需要通过Eclipse的update功能来添加就可以了,不过后者采用在线安装的方式,容易引起packge插件下载不全,导致开发时出现
莫明其妙的错误,因此推荐第一种方案。
(二)新建并运行Eclipse RCP项目
Eclipse安装并启动完毕之后,我们可以以插件的形式新增一个RCP模块,在该模块的基础上再添加不同的视图,这样就为下一步打下了良好的基础:
在Eclipse的Package Explorer中单击右键,选择菜单第一项“new”,在弹出的子菜单中选择“other”,在对话框中如下图选择:
在弹出对话框的“project name”栏中填入自己的项目名称,例如“hellorcp“,其余选项保持默认状态,点击下一步,在Plugin content对话框中“Rich Client Application”中,选择"YES",如下图所示:
选择YES表示除了新建一个插件之外,还愿意将其转换成一个RCP应用,以便以后脱离Eclipse开发环境而独立部署,而插件是不能独立运行的,必须依附Eclipse而运行。
点击下一步,选择一个模版,如下图所示:
点击Finish,在出现的最后对话框中选择“static content”,点击完成,这样就完成了向导方式下的Eclipse RCP项目hellorcp的创建,下面是该RCP项目在package explorer中的视图:
至此,我们已经创建了一个hellorcp的项目,在右边的editor窗口中,自动出现了hellorcp的启动视图,点击"Testing"中的“launch”链接,可以启动该rcp项目:
大功告成,我们得到了一个可以运行的Eclipse RCP项目,Eclipse
RCP的项目创建过程是不是非常简单呢?现在的界面就像一个白板,我们已经迫不及待地想在上面丰富我们的视图了,接下来我们的任务是创建不同的视图,并添
加代码让它们动起来,在做这些之前,介绍一下Eclipse RCP上面的一些概念:
(三)Eclipse RCP 术语介绍:
- Application
Application的作用就是应用程序或产品(product)的入口,它和Java系统中,类定义的main()方法作用一样. 当Runtime启动的时候,应用程序开始运行,应用程序退出的时候,Eclipse关闭。
在我们的hellorcp中的src文件夹中,Eclipse为我们自动生成了Application类,如下面的代码片段:
1public Object start(IApplicationContext context) {
2 Display display = PlatformUI.createDisplay();
3 try {
4 int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor());
5 if (returnCode == PlatformUI.RETURN_RESTART) {
6 return IApplication.EXIT_RESTART;
7 }
8 return IApplication.EXIT_OK;
9 } finally {
10 display.dispose();
11 }
12 }
在start方法中,首先创建了Display对象,然后使用平
台的UI线程PlatformUI启动了程序运行,而启动过程是通过传递display对象以及新建的
ApplicationWorkbenchAdvisor对象来进行的,下面就介绍一下ApplicationWorkbenchAdvisor对象
- ApplicationWorkbenchAdvisor
在
应用程序中,每一个窗口都有一个 WorkbenchWindowAdvisor ,它的作用是也是告诉窗口如何渲染.在 Window
advisors根据窗口的生命周期定义了多个切入点函数,如preWindowOpen()窗口打开前
,postWindowCreate()创建窗口结后等,可以在不同的方法创建窗口的内容,了解窗口的生命周期是很有必要的,可以让你规划后,在何时才能
创建相应的对象,有效的避免窗口中的组件还没有被创建,就已经在使用了.
1public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor {
2
3 private static final String PERSPECTIVE_ID = "hellorcp.perspective";
4
5 public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) {
6 return new ApplicationWorkbenchWindowAdvisor(configurer);
7 }
8
9 public void initialize(IWorkbenchConfigurer configurer) {
10 super.initialize(configurer);
11 configurer.setSaveAndRestore(true);
12 }
13
14 public String getInitialWindowPerspectiveId() {
15 return PERSPECTIVE_ID;
16 }
17}
在ApplicationWorkbenchAdvisor中,向导为我们创建了ApplicationWorkbenchWindowAdvisor对
象,别的什么也没做。Eclipse遵从经典的MVC模式来设计,其中Advisor类就相当于Model,相当于视图的内容提供者,这样就把view和
model解耦了。像它的名字一样, WorkbenchAdvisor 告诉工作台怎样显示,显示什么等信息。在RCP应用程序中,很多地方用这种模板模式来构建UI组件。WorkbenchAdvisor做了两件事:
初始化默认视图和创建Application的window advisor
- ApplicationWorkbenchWindowAdvisor
在
应用程序中,每一个窗口都有一个 WorkbenchWindowAdvisor ,它的作用是也是告诉窗口如何渲染.在 Window
advisors根据窗口的生命周期定义了多个切入点函数,如preWindowOpen()窗口打开前
,postWindowCreate()创建窗口后等,可以在不同的方法创建窗口的内容,了解窗口的生命周期是很有必要的,可以让你规划后,在何时才能创
建相应的对象,有效的避免窗口中的组件还没有被创建,就已经在使用了
1public ApplicationWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) {
2 super(configurer);
3 }
4
5 public ActionBarAdvisor createActionBarAdvisor(IActionBarConfigurer configurer) {
6 return new ApplicationActionBarAdvisor(configurer);
7 }
8
9 public void preWindowOpen() {
10 IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
11 configurer.setInitialSize(new Point(700, 550));
12 configurer.setShowCoolBar(false);
13 configurer.setShowStatusLine(false);
14 configurer.setTitle("RCP Product");
15 }
- ApplicationActionBarAdvisor
ActionBarAdvisors
创建了窗口需要的actions(动作),所谓动作指的就是菜单项和工具栏项。在Eclipse的开发模式中,将用户点击后系统所作的动作都可以定义为
Action,这也实现了MVC的设计模式。如果不这样做,而是在UI的控件上直接添加Action
Listener的话,界面代码和动作代码就会混杂在一起,以后维护起来就比较麻烦。因此如果要新增任何的用户交互动作,都需要在该类中的
MakeActions方法中实例化。
1protected void makeActions(IWorkbenchWindow window) {
2 introAction = ActionFactory.INTRO.create(window);
3 register(introAction);
4 }
- Perspective
顾名思义,Perspective主要定义了RCP窗口的布局,透视图类必须实现IPerspectiveFactory 接口,并在createInitial Layout(IPageLayout)方法添加实际的布局代码。在这个例子中,我们就使用默认布局。在后期创建视图的过程中,我们还要用到这个类。
- 以下是Eclipse RCP创建时上述几个对象的时序图:
(四)创建视图
如上所示,我们已经了解了Eclipse RCP上的一些概念,每个类各司其职,共同组成了美妙的窗口和布局,这就是Eclipse的魅力,在下面我们就着手创建视图,在接下来的章节中,上面介绍的概念都要用到。
视图(View 和ViewPart)
视图,视图是为了方便用户编辑提供一些辅助功能或编辑一些属性。比如最常见的Outline视
图往往用来提供当前编辑的文档的结构,而在Eclipse开发环境菜单项"window"中,选择"show
view"项,还可以看到更多的视图。如果用户使用Eclipse的PDE来开发Java程序,往往一个java文件打开,都伴随着不同视图的出现,从而
为开发人员提供了不同的观察代码及其运行方式的窗口。
下面我们就使用向导创建第一个视图:navigationView视图。
点击hellorcp项目中的plugin.xml文件,在editor中打开整个hellorcp项目的视图。这里要补充一下,plugin.xml是
整个Eclipse
RCP项目的配置文件,主要描述了插件之间的依赖关系,以及插件内部扩展的模块等等项目元数据信息。Eclipse提供了plugin.xml的可视化编
辑方式,减轻了直接编辑xml的复杂度。
一般来说,创建视图可以通过使用Eclipse的Extension机制来完成,通过继承Eclipse的Views组件,可以创建一个简单的视图。
点击“finish”,就可以在extensions视图里看到新增的org.eclipse.ui.views扩展点,右键单击该扩展点,选择新建view,如下图:
在视图右边的新建view对话框中,填入view的名称:navigationView,如下图所示:
目前我们已经在plugin.xml中定义了navigationview扩展点,现在为该navigationview添加实现类,在上图的class*文本上点击"ctl+enter",可以打开新建navigationview class的界面:
点击保存,我们现在就得到了一个navigation view,而eclipse自动在plugin.xml文件中自动添加了有关配置片段:
1<extension
2 point="org.eclipse.ui.views">
3 <view
4 class="hellorcp.NavigationViewPart"
5 id="hellorcp.navigationview"
6 name="navigationview">
7 </view>
8 </extension>
通过以上步骤,我们得到了一个视图类,现在我们就可以回到eclipse开发环境中,点击package explorer中的NavigationViewPart文件,
注意NavigationViewPart类中的createPartControl方法,视图中的表格、按钮之类的控件部分代码都是在该方法中创建的,
现在我们在该方法中创建一个List,用来浏览我们提供的对象信息。注意Eclipse
RCP是建立在jface基础上的,后者在swt的基础上对一些常用控件,例如表格、列表,树等做了封装,采用Viewer来提供视图,content
provider和input model来提供模型,label
provider来提供标签。因此要想创建列表并显示列表内容,必须同时创建上述几个对象,同时,为了填充数据模型,我们同时也创建了示例性质的
Person对象以及相关辅助代码。如果在正式的环境中,这些model可以从数据库获得,在这里我们仅做示范性使用。以下依次是创建代码:
(1)模型:Person.class 和PersonModel.class
Person.class
package hellorcp;
public class Person {
private String name;
private String sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
PersonModel.class
package hellorcp;
import java.util.ArrayList;
public class PersonModel {
private ArrayList<Person> list = new ArrayList<Person>();
public PersonModel() {
// 硬编码,初始化数据示例
Person p1 = new Person();
p1.setName("毛毛");
p1.setSex("男");
list.add(p1);
Person p2 = new Person();
p2.setName("皮皮");
p2.setSex("女");
list.add(p2);
}
public ArrayList<Person> elements() {
return list;
}
}
(2)LabelProvider
package hellorcp;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.LabelProviderChangedEvent;
import org.eclipse.swt.graphics.Image;
public class NavigationViewLabelProvider implements ILabelProvider {
public Image getImage(Object element) {
// TODO Auto-generated method stub
return null;
}
public String getText(Object element) {
// TODO Auto-generated method stub
return ((Person)element).getName();
}
public void addListener(ILabelProviderListener listener) {
// TODO Auto-generated method stub
}
public void dispose() {
// TODO Auto-generated method stub
}
public boolean isLabelProperty(Object element, String property) {
// TODO Auto-generated method stub
return false;
}
public void removeListener(ILabelProviderListener listener) {
// TODO Auto-generated method stub
}
public void labelProviderChanged(LabelProviderChangedEvent event) {
// TODO Auto-generated method stub
}
}
(3)content provider
1package hellorcp;
2
3import org.eclipse.jface.viewers.ILabelProviderListener;
4import org.eclipse.jface.viewers.IStructuredContentProvider;
5import org.eclipse.jface.viewers.ListViewer;
6import org.eclipse.jface.viewers.Viewer;
7
8public class NavigationListViewContentProvider implements IStructuredContentProvider{
9 PersonModel input;
10 ListViewer viewer;
11
12 public Object[] getElements(Object inputElement) {
13 // TODO Auto-generated method stub
14 return input.elements().toArray();
15 }
16
17 public String getText(Object element) {
18 return ((Person) element).getName();
19 }
20
21 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
22 viewer = (ListViewer) viewer;
23 input = (PersonModel) newInput;
24
25 }
26
27 public void addListener(ILabelProviderListener listener) {
28 // TODO 自动生成方法存根
29
30 }
31
32 public void add(Person p) {
33 viewer.add(p);
34
35 }
36
37 public void remove(Person p) {
38 viewer.remove(p);
39
40 }
41
42 public void dispose() {
43 // TODO Auto-generated method stub
44
45 }
46}
47
(4)以下navigation view部分的代码
1package hellorcp;
2
3import org.eclipse.jface.action.IToolBarManager;
4import org.eclipse.jface.viewers.IStructuredContentProvider;
5import org.eclipse.jface.viewers.ListViewer;
6import org.eclipse.swt.SWT;
7import org.eclipse.swt.widgets.Composite;
8import org.eclipse.ui.part.ViewPart;
9import hellorcp.NavigationViewLabelProvider;
10
11public class NavigationViewPart extends ViewPart {
12 public NavigationViewPart() {
13 // TODO Auto-generated constructor stub
14 }
15
16 @Override
17 public void createPartControl(Composite parent) {
18 final ListViewer listViewer = new ListViewer(parent, SWT.BORDER);
19 listViewer.setLabelProvider(new NavigationViewLabelProvider());
20 listViewer.setContentProvider(new NavigationListViewContentProvider());
listViewer.setInput(new PersonModel());
21 initializeToolBar();
22 // TODO Auto-generated method stub
23
24 }
25
26 @Override
27 public void setFocus() {
28 // TODO Auto-generated method stub
29
30 }
31 private void initializeToolBar() {
32 IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
33 }
34
35}
36
(5)最后,我们将创建的navigationview添加到perspective透视图中
1package hellorcp;
2
3import org.eclipse.jface.action.IToolBarManager;
4import org.eclipse.jface.viewers.IStructuredContentProvider;
5import org.eclipse.jface.viewers.ListViewer;
6import org.eclipse.swt.SWT;
7import org.eclipse.swt.widgets.Composite;
8import org.eclipse.ui.part.ViewPart;
9import hellorcp.NavigationViewLabelProvider;
10
11public class NavigationViewPart extends ViewPart {
12 public NavigationViewPart() {
13 // TODO Auto-generated constructor stub
14 }
15
16 @Override
17 public void createPartControl(Composite parent) {
18 final ListViewer listViewer = new ListViewer(parent, SWT.BORDER);
19 listViewer.setLabelProvider(new NavigationViewLabelProvider());
20 listViewer.setContentProvider(new NavigationListViewContentProvider());
21 listViewer.setInput(new PersonModel());
22 initializeToolBar();
23 // TODO Auto-generated method stub
24
25 }
26
27 @Override
28 public void setFocus() {
29 // TODO Auto-generated method stub
30
31 }
32 private void initializeToolBar() {
33 IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
34 }
35
36}
37
至此,我们已经创建了一个可以浏览人员信息的navigation
view,运行一下我们创建的视图,可以得到如下效果,注意运行前需要将eclipse文件夹中以runtime...开头的文件夹删除,这是
eclipse上次运行时创建的临时文件,如果不删除,就看不到修改之后的效果:
OK,现在我们已经创建了一个视图,我们还需要创建另外一个details视图,用来显示人员的若干详细信息,按照上述创建视图的方法,我们同样可以创建该视图,以下就不赘述了,只贴出创建detail view视图的代码和perspective透视图部分的代码:
(一)Detail view
1package hellorcp;
2
3import java.beans.PropertyChangeEvent;
4import java.util.AbstractList;
5import java.util.ArrayList;
6import java.util.Iterator;
7
8import org.eclipse.core.runtime.Preferences.IPropertyChangeListener;
9import org.eclipse.jface.action.IToolBarManager;
10import org.eclipse.jface.viewers.ISelection;
11import org.eclipse.jface.viewers.IStructuredSelection;
12import org.eclipse.jface.viewers.ISelectionChangedListener;
13import org.eclipse.jface.viewers.SelectionChangedEvent;
14import org.eclipse.jface.viewers.TableViewer;
15import org.eclipse.swt.SWT;
16import org.eclipse.swt.events.SelectionAdapter;
17import org.eclipse.swt.widgets.Composite;
18import org.eclipse.swt.widgets.Group;
19import org.eclipse.swt.widgets.Label;
20import org.eclipse.swt.widgets.Table;
21import org.eclipse.swt.widgets.Text;
22import org.eclipse.ui.ISelectionListener;
23import org.eclipse.ui.IWorkbenchPart;
24import org.eclipse.ui.part.ViewPart;
25
26public class DetailView extends ViewPart {
27
28 private Table table;
29 private Text text_1;
30 private Text text;
31 @SuppressWarnings("unchecked")
32 ArrayList myListeners;
33 private ISelection selection;
34 public DetailView() {
35 super();
36 // TODO Auto-generated constructor stub
37 }
38
39 @Override
40 public void createPartControl(Composite parent) {
41 final Composite composite = new Composite(parent, SWT.NONE);
42
43 final Group group = new Group(composite, SWT.NONE);
44 group.setBounds(0, 0, 494, 62);
45
46 final Label label = new Label(group, SWT.NONE);
47 label.setText("姓名");
48 label.setBounds(10, 32, 24, 12);
49
50 text = new Text(group, SWT.BORDER);
51 text.setBounds(40, 29, 80, 15);
52
53 final Label label_1 = new Label(group, SWT.NONE);
54 label_1.setText("性别");
55 label_1.setBounds(172, 32, 30, 12);
56
57 text_1 = new Text(group, SWT.BORDER);
58 text_1.setBounds(208, 29, 80, 15);
59
60 final TableViewer tableViewer = new TableViewer(composite, SWT.BORDER);
61 table = tableViewer.getTable();
62 table.setLinesVisible(true);
63 table.setHeaderVisible(true);
64 table.setBounds(0, 68, 494, 270);
65 initializeToolBar();
66 }
67
68 @Override
69 public void setFocus() {
70 // TODO Auto-generated method stub
71
72 }
73 private void initializeToolBar() {
74 IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
75 }
76
77}
78
(二)添加过detailview视图的perspective透视类:
1package hellorcp.intro;
2
3import org.eclipse.ui.IPageLayout;
4import org.eclipse.ui.IPerspectiveFactory;
5
6public class Perspective implements IPerspectiveFactory {
7
8 public void createInitialLayout(IPageLayout layout) {
9 String editorArea = layout.getEditorArea();
10 layout.addView("hellorcp.navigationview", IPageLayout.LEFT, 0.15f, editorArea);
11 layout.addView("hellorcp.detailview", IPageLayout.LEFT, 1f, editorArea);
12 }
13}
以上步骤完成之后,我们第一阶段的工作就完毕了。让我们回顾一下,采取上述一系列步骤后,我们创建了一个简单的RCP应用,具备两个视图
navigation view和detail
view,前者以列表的形式显示人员信息,当用户点击列表上某一人员信息元素时,后者能够显示出该人员的详细信息,就这么简单,但是别忘了我们只完成了显
示部分,而最重要、也是相对较为复杂的视图间消息传递部分,我们将放在后续部分继续讲解。
以下是我们完成的视图,检视一下,第一个RCP程序从自己手中诞生了,是不是有种成功的喜悦呢?
使用Eclipse RCP创建视图并实现视图间消息传递(二)
在上一文中,我们介绍了怎样创建一个Eclipse
RCP,并创建用户交互的视图,但是并没有提到如何实现视图的交互。在本文中,我们会介绍以两种不同的方式实现Eclipse RCP
View之间的交互,并结合代码详细领略一下Eclipse是如何巧妙处理View之间交互的。
首先介绍一下我们要达到的效果,如下图所示:
可以看到,用户点击左边视图上的人员列表,该人员的详细信息会显示在右侧的view视图中,这一步骤看起来简单,但是要做的工作却不少,下面我们就着手把前期获得的RCP程序做成这个样子,让View之间传递信息或者更复杂一些的对象。
从Eclipse目前提供的手段看来,我们可以使用下列方法实现视图的交互:
(1)
选择提供器 - 选择监听器(selection provider-selection listener)模式,从而让视图对其他视图中的选择做出反应
(2)使用属性改变监听模式,即Property Changed Listener模式,用于视图中没有可供点击的UI模块的情况下。这类监听模式允许视图将属性改变事件
以下将详细介绍这两种模式的概念、适用场景,并结合我们的Hello RCP分别实例说明这两种视图交互模式的实现。
(一)选择提供器-选择监听器(selection provider-selection listener)模式:
这是一种传统的事件传递模式,一般来说,只要视图中的UI控件实现了ISelectionProvider接口,即具备提供选
择事件的能力,就可以使用这个模式将自身发生的选择事件提供给其他实现了ISelectionListener的UI控件。例如我们在Hello
RCP中实现的Navigation视图,里面采用了ListViewer这一控件,而Jface中所有的Viewer对象都实现了
ISelectionProvider接口,那么当我们点击navigation视图list列表中的人员名单时,ListViewer列表就可以向其他
能够监听该选择事件的对象提供该选择事件,如果我们在事件中包含一些字符串信息,那么其他对象得到这个信息之后,就可以进行相应显示了。
“Eclipse
提供了所谓的Site
,以及ISelectionService机制,来处理试图之间的简单的交互。简单的说,ViewSite
提供了一个交互的中心点,其它View
向ViewSite
提供选择事件,或者向其注册监听器,而事件的触发与转发则由ViewSite()
来完成。”
以上只是一些概念介绍,对于我们来说,只需按照下列步骤进行,就可以将一个视图变成一个提供Selection 事件的Selection Provider:
(1)首先将视图注册成为Selection Provider,即实现ISelectionProvider接口
public class NavigationView extends ViewPart implements ISelectionProvider
以下是ISelectionProvider接口的定义:
1public interface ISelectionProvider {
2
3 public void addSelectionChangedListener(ISelectionChangedListener listener);
4
5 public ISelection getSelection();
6
7 public void removeSelectionChangedListener(
8
9 ISelectionChangedListener listener);
10
11 public void setSelection(ISelection selection);
12
13 }
14
(2)有了以上步骤还不够,还需要将视图中具体的Viewer上想要发生的事件,注册到这个Provider上。
viewer.addSelectionChangedListener(new ISelectionChangedListener(){
public void selectionChanged(SelectionChangedEvent event) {
ISelection selection2 = event.getSelection();
setSelection(selection2);
}
});
这段代码就是把ListViewer上的Selection Change Event注册到了Selection Provider上,由后者负责向外传播
(3)一个Selection Provider想要其他部件监听的话,还要向Site中控台进行注册,要不然就相当于开会时找不到话筒讲话,大家听不见
1this.getSite().setSelectionProvider(this);
注意this对象指的是Viwe视图,它具有指向Site的引用,通过getSite()方法获得引用
(4)最后,还要把讲给听众听,没有听众也是白讲,这里是在setSelection()方法里迭代每一个注册了ISelectionListener的控件,找到它们,把事件传递给这些听众即可:
public void setSelection(ISelection selection) {
this.selection = selection;
SelectionChangedEvent event2 = new SelectionChangedEvent(viewer, selection);
for (Iterator i = myListeners.iterator(); i.hasNext();) {
ISelectionChangedListener object = (ISelectionChangedListener) i.next();
object.selectionChanged(event2);
}
}
至此,我们已经实现了一个Selection Provider了,对于事件监听的另一端,即Selection Listener,则更为简单一些。只需要实现ISelectionListener
接口,并注册在Site
中:
site.getPage().addSelectionListener(this);
然后实现public void selectionChanged(IWorkbenchPart part, ISelection selection) {}方法即可。这样,当SelectionProvider中的选择发生改变时,这个视图中的selectionChanged()方法就会被调用。
即如下方式:
1public void selectionChanged(IWorkbenchPart part, ISelection selection) {
2 IStructuredSelection structuredSelection = (IStructuredSelection)selection;
3 Object obj = structuredSelection.getFirstElement();
4 Person tempPerson = (Person)obj;
5 if(tempPerson != null)
6 text.setText(tempPerson.getName());
7 text_1.setText(tempPerson.getSex());
8 }
根据以上介绍的Selection Provider-Selection Listener模式,我们重新改造了一下navigation view视图和detail view视图:
1package hellorcp;
2
3import java.util.ArrayList;
4import java.util.Iterator;
5
6import org.eclipse.jface.action.IToolBarManager;
7import org.eclipse.jface.viewers.ISelection;
8import org.eclipse.jface.viewers.ISelectionChangedListener;
9import org.eclipse.jface.viewers.ISelectionProvider;
10import org.eclipse.jface.viewers.IStructuredContentProvider;
11import org.eclipse.jface.viewers.ListViewer;
12import org.eclipse.jface.viewers.SelectionChangedEvent;
13import org.eclipse.swt.SWT;
14import org.eclipse.swt.widgets.Composite;
15import org.eclipse.ui.part.ViewPart;
16import hellorcp.NavigationViewLabelProvider;
17
18public class NavigationViewPart extends ViewPart implements ISelectionProvider{
19
20 private ISelection selection;
21 ArrayList myListeners = new ArrayList();
22 ListViewer listViewer ;
23
24 public NavigationViewPart() {
25 // TODO Auto-generated constructor stub
26 }
27
28 @Override
29 public void createPartControl(Composite parent) {
30 listViewer = new ListViewer(parent, SWT.BORDER);
31 listViewer.setLabelProvider(new NavigationViewLabelProvider());
32 listViewer.setContentProvider(new NavigationListViewContentProvider());
33 listViewer.setInput(new PersonModel());
34 initializeToolBar();
35 listViewer.addSelectionChangedListener(new ISelectionChangedListener(){
36 public void selectionChanged(SelectionChangedEvent event) {
37 ISelection selection2 = event.getSelection();
38 setSelection(selection2);
39 }
40 });
41 this.getSite().setSelectionProvider(this);
42 // TODO Auto-generated method stub
43
44 }
45
46 public ISelection getSelection() {
47 // TODO Auto-generated method stub
48 return null;
49 }
50
51 public void addSelectionChangedListener(ISelectionChangedListener listener) {
52 if(!myListeners.contains(listener))
53 myListeners.add(listener);
54 }
55
56 public void removeSelectionChangedListener(ISelectionChangedListener listener) {
57 myListeners.remove(listener);
58 }
59
60 @Override
61 public void setFocus() {
62 // TODO Auto-generated method stub
63
64 }
65 private void initializeToolBar() {
66 IToolBarManager toolBarManager = getViewSite().getActionBars().getToolBarManager();
67 }
68 public void setSelection(ISelection selection) {
69 this.selection = selection;
70 SelectionChangedEvent event2 = new SelectionChangedEvent(listViewer, selection);
71 for (Iterator i = myListeners.iterator(); i.hasNext();) {
72 ISelectionChangedListener object = (ISelectionChangedListener) i.next();
73 object.selectionChanged(event2);
74 }
75
76 }
77
78}
79
1package hellorcp;
2
3import java.util.ArrayList;
4
5import org.eclipse.jface.viewers.ISelection;
6import org.eclipse.jface.viewers.IStructuredSelection;
7import org.eclipse.jface.viewers.TableViewer;
8import org.eclipse.swt.SWT;
9import org.eclipse.swt.widgets.Composite;
10import org.eclipse.swt.widgets.Group;
11import org.eclipse.swt.widgets.Label;
12import org.eclipse.swt.widgets.Table;
13import org.eclipse.swt.widgets.Text;
14import org.eclipse.ui.ISelectionListener;
15import org.eclipse.ui.IWorkbenchPart;
16import org.eclipse.ui.part.ViewPart;
17
18public class DetailView extends ViewPart implements ISelectionListener{
19
20 private Table table;
21 private Text text_1;
22 private Text text;
23 @SuppressWarnings("unchecked")
24 ArrayList myListeners;
25 public DetailView() {
26 super();
27 // TODO Auto-generated constructor stub
28 }
29
30 @Override
31 public void createPartControl(Composite parent) {
32 final Composite composite = new Composite(parent, SWT.NONE);
33
34 final Group group = new Group(composite, SWT.NONE);
35 group.setBounds(0, 0, 494, 62);
36
37 final Label label = new Label(group, SWT.NONE);
38 label.setText("姓名");
39 label.setBounds(10, 32, 24, 12);
40
41 text = new Text(group, SWT.BORDER);
42 text.setBounds(40, 29, 80, 15);
43
44 final Label label_1 = new Label(group, SWT.NONE);
45 label_1.setText("性别");
46 label_1.setBounds(172, 32, 30, 12);
47
48 text_1 = new Text(group, SWT.BORDER);
49 text_1.setBounds(208, 29, 80, 15);
50
51 final TableViewer tableViewer = new TableViewer(composite, SWT.BORDER);
52 table = tableViewer.getTable();
53 table.setLinesVisible(true);
54 table.setHeaderVisible(true);
55 table.setBounds(0, 68, 494, 270);
56 initializeToolBar();
57 this.getSite().getPage().addSelectionListener(this);
58 }
59
60 @Override
61 public void setFocus() {
62 // TODO Auto-generated method stub
63
64 }
65 private void initializeToolBar() {
66 }
67
68 public void selectionChanged(IWorkbenchPart part, ISelection selection) {
69 IStructuredSelection structuredSelection = (IStructuredSelection)selection;
70 Object obj = structuredSelection.getFirstElement();
71 Person tempPerson = (Person)obj;
72 if(tempPerson != null)
73 text.setText(tempPerson.getName());
74 text_1.setText(tempPerson.getSex());
75
76 }
77
78}
79
将以上文件重新编译,再运行我们的Hello RCP,可以看到,点击Navigation Views时,已经可以将选择的人员信息传递给detal view了,我们的视图真正动了起来:
其实对于本例来说,由于JFace Viewer已经实现了ISelectionProvider
接口,因此还有一种更简便的方法实现上述效果,就是将navigation view中的list viewer直接注册为Selection
Provider ,这样就可以省去实现ISelectionProvider接口的代码了:
以下是
NavigationViewPart.java的另外一种实现方式:
1package hellorcp;
2
3import java.util.ArrayList;
4import java.util.Iterator;
5
6import org.eclipse.jface.action.IToolBarManager;
7import org.eclipse.jface.viewers.ISelection;
8import org.eclipse.jface.viewers.ISelectionChangedListener;
9import org.eclipse.jface.viewers.ISelectionProvider;
10import org.eclipse.jface.viewers.IStructuredContentProvider;
11import org.eclipse.jface.viewers.ListViewer;
12import org.eclipse.jface.viewers.SelectionChangedEvent;
13import org.eclipse.swt.SWT;
14import org.eclipse.swt.widgets.Composite;
15import org.eclipse.ui.part.ViewPart;
16import hellorcp.NavigationViewLabelProvider;
17
18public class NavigationViewPart extends ViewPart{
19
20 private ISelection selection;
21 ArrayList myListeners = new ArrayList();
22 ListViewer listViewer ;
23
24 public NavigationViewPart() {
25 // TODO Auto-generated constructor stub
26 }
27
28 @Override
29 public void createPartControl(Composite parent) {
30 listViewer = new ListViewer(parent, SWT.BORDER);
31 listViewer.setLabelProvider(new NavigationViewLabelProvider());
32 listViewer.setContentProvider(new NavigationListViewContentProvider());
33 listViewer.setInput(new PersonModel());
34 this.getSite().setSelectionProvider(listViewer);
35 // TODO Auto-generated method stub
36
37 }
38 @Override
39 public void setFocus() {
40 // TODO Auto-generated method stub
41
42 }
43
44}
45
对于detail view ,如果将将消费者视图作为监听器注册到特定的视图部分,就不用循环所有的Listener来通知了,这样可以节约更多的系统资源了。
this.getSite().getPage().addSelectionListener("hellorcp.navigationview",(ISelectionListener)this);
以下是
DetailView的另外一种实现:
1package hellorcp;
2
3import java.util.ArrayList;
4
5import org.eclipse.jface.viewers.ISelection;
6import org.eclipse.jface.viewers.IStructuredSelection;
7import org.eclipse.jface.viewers.TableViewer;
8import org.eclipse.swt.SWT;
9import org.eclipse.swt.widgets.Composite;
10import org.eclipse.swt.widgets.Group;
11import org.eclipse.swt.widgets.Label;
12import org.eclipse.swt.widgets.Table;
13import org.eclipse.swt.widgets.Text;
14import org.eclipse.ui.ISelectionListener;
15import org.eclipse.ui.IWorkbenchPart;
16import org.eclipse.ui.part.ViewPart;
17
18public class DetailView extends ViewPart implements ISelectionListener{
19
20 private Table table;
21 private Text text_1;
22 private Text text;
23 @SuppressWarnings("unchecked")
24 ArrayList myListeners;
25 public DetailView() {
26 super();
27 // TODO Auto-generated constructor stub
28 }
29
30 @Override
31 public void createPartControl(Composite parent) {
32 final Composite composite = new Composite(parent, SWT.NONE);
33
34 final Group group = new Group(composite, SWT.NONE);
35 group.setBounds(0, 0, 494, 62);
36
37 final Label label = new Label(group, SWT.NONE);
38 label.setText("姓名");
39 label.setBounds(10, 32, 24, 12);
40
41 text = new Text(group, SWT.BORDER);
42 text.setBounds(40, 29, 80, 15);
43
44 final Label label_1 = new Label(group, SWT.NONE);
45 label_1.setText("性别");
46 label_1.setBounds(172, 32, 30, 12);
47
48 text_1 = new Text(group, SWT.BORDER);
49 text_1.setBounds(208, 29, 80, 15);
50
51 final TableViewer tableViewer = new TableViewer(composite, SWT.BORDER);
52 table = tableViewer.getTable();
53 table.setLinesVisible(true);
54 table.setHeaderVisible(true);
55 table.setBounds(0, 68, 494, 270);
56 initializeToolBar();
57 this.getSite().getPage().addSelectionListener("hellorcp.navigationview",(ISelectionListener)this);
58
59 }
60
61 @Override
62 public void setFocus() {
63 // TODO Auto-generated method stub
64
65 }
66 private void initializeToolBar() {
67 }
68
69 public void selectionChanged(IWorkbenchPart part, ISelection selection) {
70 IStructuredSelection structuredSelection = (IStructuredSelection)selection;
71 Object obj = structuredSelection.getFirstElement();
72 Person tempPerson = (Person)obj;
73 if(tempPerson != null)
74 text.setText(tempPerson.getName());
75 text_1.setText(tempPerson.getSex());
76
77 }
78
79}
80
看起来后两种实现是不是比前面的要轻便很多呢?
但是对于我们来讲,视图间以Selection Provider 和Listener模式传递消息还存在很多局限:
1 视图可能希望公布其他信息,而不只是公布可视化选择信息。公布的信息可能是根据选择进行某些后期处理的结果。
2 视图可能希望使用来自另一个插件的信息,而这个插件可能根本没有提供视图(使用包含的 JFace 查看器)。在这种情况下,使用基于 UI 选择的链接是不可能的
对于以上问题,通过自动方式进行就很困难了,需要我们手工解决,而为视图注册属性监听器而实现观测其它视图的属性变化是个不错的替代模式。
(二)属性改变监听器模式:
属性改变监听器模式主要通过使用JFace中一个重要的接口org.eclipse.jface.util.IPropertyChangeListener来实现,通过注册该监听器,以及使用IPropertyChangeEvent,可以达到事件传递的目的。
与Selection provider-listener模式不同的是,属性改变监听器可以定义到插件上,由插件本身提供注册列表,如下所示:
1ArrayList myListeners = new ArrayList();
2 // A public method that allows listener registration
3 public void addPropertyChangeListener(IPropertyChangeListener listener) {
4 if(!myListeners.contains(listener))
5 myListeners.add(listener);
6 }
7 // A public method that allows listener registration
8 public void removePropertyChangeListener(IPropertyChangeListener listener) {
9 myListeners.remove(listener);
10 }
当插件想要把产生的事件通知到各个监听器的时候,就对这个注册列表中每个Listener元素进行迭代,逐一通知,这样每个Property Change Event就传播到了各个Listener,由后者进行处理了,这个调用模式如下所示:
1public void initAndInvoke(ArrayList listeners, Object obj) {
2 // Post Invocation, inform listeners
3 for (Iterator<IPropertyChangeListener> iter = myListeners.iterator(); iter.hasNext();) {
4 IPropertyChangeListener element = (IPropertyChangeListener) iter.next();
5 element.propertyChange(new PropertyChangeEvent(this, "HelloRcpEvent" , null , obj));
6
7 }
8 }
总而言之,要想实现这个模式,我们必须自己新定义一个注册并调用Property Change Event的类,我们把它叫做:PersonPlugin.java
那么接下来对于我们的navigation view中的list来说,由于没有采用provider模式,那么就必须自己定义它的SelectionChangeListener了,我们可以在这个ListViewer的添加该Listener中实现我们的目的:
1package hellorcp;
2
3import java.util.ArrayList;
4import java.util.Iterator;
5
6import org.eclipse.jface.action.IToolBarManager;
7import org.eclipse.jface.util.IPropertyChangeListener;
8import org.eclipse.jface.viewers.ISelection;
9import org.eclipse.jface.viewers.ISelectionChangedListener;
10import org.eclipse.jface.viewers.ISelectionProvider;
11import org.eclipse.jface.viewers.IStructuredContentProvider;
12import org.eclipse.jface.viewers.IStructuredSelection;
13import org.eclipse.jface.viewers.ListViewer;
14import org.eclipse.jface.viewers.SelectionChangedEvent;
15import org.eclipse.swt.SWT;
16import org.eclipse.swt.widgets.Composite;
17import org.eclipse.ui.part.ViewPart;
18
19import hellorcp.Person;
20import hellorcp.PersonPlugin;
21import hellorcp.NavigationViewLabelProvider;
22
23public class NavigationViewPart extends ViewPart{
24
25 private ISelection selection;
26 ArrayList myListeners = new ArrayList();
27 ListViewer listViewer ;
28
29 public NavigationViewPart() {
30 // TODO Auto-generated constructor stub
31 }
32
33 @Override
34 public void createPartControl(Composite parent) {
35 listViewer = new ListViewer(parent, SWT.BORDER);
36 listViewer.setLabelProvider(new NavigationViewLabelProvider());
37 listViewer.setContentProvider(new NavigationListViewContentProvider());
38 listViewer.setInput(new PersonModel());
39// this.getSite().setSelectionProvider(listViewer);
40 listViewer.addSelectionChangedListener(new ISelectionChangedListener(){
41 public void selectionChanged(SelectionChangedEvent event) {
42 ISelection selection2 = event.getSelection();
43 setSelection(selection2);
44 // Post Invocation, inform listeners
45 IStructuredSelection structuredSelection = (IStructuredSelection)selection;
46 Object obj = structuredSelection.getFirstElement();
47 Person tempPerson = (Person)obj;
48 PersonPlugin.getInstance().initAndInvoke(myListeners, tempPerson);
49 }
50 });
51 // TODO Auto-generated method stub
52
53 }
54
55 public ISelection getSelection() {
56 // TODO Auto-generated method stub
57 return null;
58 }
59
60 // A public method that allows listener registration
61 public void addPropertyChangeListener(IPropertyChangeListener listener) {
62 if(!myListeners.contains(listener))
63 myListeners.add(listener);
64 }
65
66 // A public method that allows listener registration
67 public void removePropertyChangeListener(IPropertyChangeListener listener) {
68 myListeners.remove(listener);
69 }
70
71 @Override
72 public void setFocus() {
73 // TODO Auto-generated method stub
74
75 }
76 public void setSelection(ISelection selection) {
77 this.selection = selection;
78 }
79}
80
注意我们为listviewer添加了Selection Change Listener 方法,并通知到了PersonPlugin。
接着,在detail view中,我们只用实现IPropertyChangeListener即可,以下是detail view的代码:
1package hellorcp;
2
3import java.util.ArrayList;
4
5import org.eclipse.jface.util.IPropertyChangeListener;
6import org.eclipse.jface.viewers.ISelection;
7import org.eclipse.jface.viewers.IStructuredSelection;
8import org.eclipse.jface.viewers.TableViewer;
9import org.eclipse.swt.SWT;
10import org.eclipse.swt.widgets.Composite;
11import org.eclipse.swt.widgets.Group;
12import org.eclipse.swt.widgets.Label;
13import org.eclipse.swt.widgets.Table;
14import org.eclipse.swt.widgets.Text;
15import org.eclipse.ui.ISelectionListener;
16import org.eclipse.ui.IWorkbenchPart;
17import org.eclipse.ui.part.ViewPart;
18
19import hellorcp.PersonPlugin;
20
21import hellorcp.Person;
22
23public class DetailView extends ViewPart implements IPropertyChangeListener{
24
25 private Table table;
26 private Text text_1;
27 private Text text;
28 @SuppressWarnings("unchecked")
29 ArrayList myListeners;
30 public DetailView() {
31 super();
32 // TODO Auto-generated constructor stub
33 }
34
35 @Override
36 public void createPartControl(Composite parent) {
37 final Composite composite = new Composite(parent, SWT.NONE);
38
39 final Group group = new Group(composite, SWT.NONE);
40 group.setBounds(0, 0, 494, 62);
41
42 final Label label = new Label(group, SWT.NONE);
43 label.setText("姓名");
44 label.setBounds(10, 32, 24, 12);
45
46 text = new Text(group, SWT.BORDER);
47 text.setBounds(40, 29, 80, 15);
48
49 final Label label_1 = new Label(group, SWT.NONE);
50 label_1.setText("性别");
51 label_1.setBounds(172, 32, 30, 12);
52
53 text_1 = new Text(group, SWT.BORDER);
54 text_1.setBounds(208, 29, 80, 15);
55
56 final TableViewer tableViewer = new TableViewer(composite, SWT.BORDER);
57 table = tableViewer.getTable();
58 table.setLinesVisible(true);
59 table.setHeaderVisible(true);
60 table.setBounds(0, 68, 494, 270);
61 initializeToolBar();
62 //注册事件
63 PersonPlugin.getInstance().addPropertyChangeListener(this);
64
65 }
66
67 @Override
68 public void setFocus() {
69 // TODO Auto-generated method stub
70
71 }
72 private void initializeToolBar() {
73 }
74
75// public void selectionChanged(IWorkbenchPart part, ISelection selection) {
76// IStructuredSelection structuredSelection = (IStructuredSelection)selection;
77// Object obj = structuredSelection.getFirstElement();
78// Person tempPerson = (Person)obj;
79// if(tempPerson != null)
80// text.setText(tempPerson.getName());
81// text_1.setText(tempPerson.getSex());
82//
83// }
84 public void propertyChange(org.eclipse.jface.util.PropertyChangeEvent event) {
85 if( event.getProperty().equals("HelloRcpEvent")) {
86 Object val = event.getNewValue();
87 Person tempPerson = (Person)val;
88 if(tempPerson != null){
89 text.setText(tempPerson.getName());
90 text_1.setText(tempPerson.getSex());
91 }
92 }
93
94 }
95
96}
97
采用属性改变监听模式能够更加灵活地在插件之间、插件的各个view之间传递信息,突破了传递信息必须与UI相关的局限,而且还可以异步传递信息,这些信
息可以是插件后台JOB定期运行获得的信息,以及定期从数据库中获得的信息等等。不难看到,属性改变监听器扩展了插件之间,视图之间,前台与后台之间的消
息传递场景,是我们开发Eclipse RCP应用更好的选择。