程序|链接|视图
丰富的 GUI 中的视图可以以各种方式显示信息,从而改善用户体验。非常自然,UI 视图之间是相互依赖的,需要进行交互。Eclipse 简化了链接 UI 视图的工作,并提供了将视图链接应用到非 UI 场景的方式。
简介
Eclipse 平台允许使用可插入组件 —— 插件 —— 帮助创建丰富的图形用户界面(graphical user interface,GUI)应用程序。例如,插件可以向 GUI 提供视图。但是,在现实的应用程序中,UI 视图不能是孤立的。它们需要根据其他视图的状态进行交互和对本身进行更新。
一个简单的例子是描述世界各地的主要旅游目的地的 GUI 应用程序。这个 GUI 可能有一个 Select City 视图,用于显示旅游景点和公共交通信息。
图 1. 视图链接的例子
本文介绍在 Eclipse 中结合视图的方式以及如何对其他视图的状态做出响应。还讨论链接视图方式在哪些情况下可能比其他方式合适。
Eclipse 开发人员可以依赖以下方法对视图进行链接:
选择提供器 - 选择监听器(selection provider-selection listener)模式,从而让视图对其他视图中的选择做出反应
IAdaptable 接口,与某些事件结合使用
属性改变监听器,它允许视图将属性改变事件告之已注册的监听器
选择提供器 - 选择监听器范型
选择提供器 - 选择监听器模式能够方便地创建对其他视图中的改变做出响应的视图。例如,当用户点击代表城市名的 UI 项时,另一个视图可能需要显示这个城市的景点详情。这样的视图可以使用 UI 选择对象(可能是代表城市名的字符串对象)中包含的信息,并使用它从模型中获取和显示其他信息。
视图应该能够识别并利用 UI 选择事件。org.eclipse.ui.ISelectionListener 是接收 UI 选择事件的监听器接口。选择监听器必须注册到工作台页面。工作台页面实现 org.eclipse.ui.ISelectionService 接口定义的服务,从而将 UI 选择事件告之监听器。选择监听器必须注册到选择服务。
用于显示可选择的 UI 项的视图还应该能够公布 UI 选择。视图通过将 “选择提供器” 注册到它们各自的工作台站点来实现这一点。Eclipse 中的每个 UI 部分通过 org.eclipse.ui.IWorkbenchPartSite 引用与工作台站点联络。选择提供器注册到工作台站点。
在使用选择提供器 - 选择监听器模式链接视图时,视图可以将本身作为监听器添加到工作台页面,而希望公布选择的其他视图必须将选择提供器添加到它们各自的工作台站点。org.eclipse.ui.ISelectionListener 接口如下所示。
public void selectionChanged(IWorkbenchPart part, ISelection selection);
要使视图能够监听选择改变,视图必须实现 ISelectionListener 接口并必须将自己注册到工作台页面。清单 1 显示一个例子。
清单 1. 将选择监听器添加到工作台页面
public class MyView extends ViewPart implements ISelectionListener{
public void createPartControl(Composite parent) {
// add this view as a selection listener to the workbench page
getSite().getPage().addSelectionListener((ISelectionListener) this);
}
// Implement the method defined in ISelectionListener, to consume UI selections
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
//Examine selection and act on it!
}
}
使用 UI 选择的更好的方法是,将消费者视图作为监听器注册到特定的视图部分。正如在下面的例子中可以看到的,源视图部分的视图 ID 在注册选择监听器期间被作为一个参数。
getSite().getPage().addSelectionListener("SampleViewId",(ISelectionListener)this);
这种方式可以避免对消费者视图进行多余的回调,如果视图被注册为非特定的监听器,就会出现这种情况。 清单 2 中的代码片段显示一个视图的 createPartControl() 方法,这个方法创建一个 JFace TableViewer 并将它作为选择提供器添加到工作台站点。这些代码使 TableViewer 中的任何 UI 选择改变能够传播到页面,并最终传播到对这种事件感兴趣的消费者视图。
清单 2. 设置选择提供器
public void createPartControl(Composite parent) {
// Set up a JFace Viewer
viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
viewer.setContentProvider(new ViewContentProvider());
viewer.setLabelProvider(new ViewLabelProvider());
viewer.setSorter(new NameSorter());
viewer.setInput(getViewSite());
// ADD the JFace Viewer as a Selection Provider to the View site.
getSite().setSelectionProvider(viewer);
}
这个视图将它创建的 JFace TableViewer 注册为选择提供器有两个原因:
这个视图打算使用这个 TableViewer 显示信息,而且用户将与 TableViewer 进行交互。
TableViewer 实现了选择提供器接口并能够向工作台部分站点传播选择事件。
因为 JFace 查看器是选择提供器,所以在大多数情况下就不必创建选择提供器了。视图只需使用众多的 JFace 查看器之一来显示信息,并将 JFace 查看器注册为选择提供器。
另一种链接方式
某些情况需要另一种视图链接方式:
信息量可能太大,由于内存使用量增加,UI 选择对象无法有效地容纳它。
视图可能希望公布其他信息,而不只是公布可视化选择信息。公布的信息可能是根据选择进行某些后期处理的结果。
视图可能希望使用来自另一个插件的信息,而这个插件可能根本没有提供视图(使用包含的 JFace 查看器)。在这种情况下,使用基于 UI 选择的链接是不可能的。
可以使用 org.eclipse.core.runtime.IAdaptable 接口来缓解第一个问题,这个接口使选择对象能够在需要时传播更多信息。第二个和第三个问题需要用手工方式解决,属性改变监听器模式是合适的解决方案。
使用 IAdaptable 接口
实现 IAdaptable 接口的类能够动态地返回某些类型的适配器,然后可以使用这些适配器获取更多信息。如果查看器中的选择对象实现了 IAdaptable 接口,那么根据它们可以返回的适配器类型,可以有效地获取更多信息或相关信息。org.eclipse.core.runtime.IAdaptable 接口如下所示。
public void object getAdapter(Class adapter);
显然,调用者应该知道它期望选择返回的适配器接口类型。考虑一个 JFace TreeViewer,它在一个单层的树中显示城市。代表城市的对象是 CityClass 类型的。CityClass 对象应该包含关于此城市的基本信息,并只在需要时返回详细信息。在清单 3 中要注意,CityClass 支持的适配器类型使调用者能够在需要时获得更多信息。
清单 3. JFace TreeViewer 中的 CityClass
class CityClass implements IAdaptable {
private String cityName;
public CityClass(String name) {
this.name = name;
}
public String getName() {
return name;
}
public CityClass getParent() {
return parent;
}
public String toString() {
return getName();
}
public Object getAdapter(Class key) {
if (key.getName().equals("ITransportationInfo"))
return CityPlugin.getInstance().getTransportAdapter();
else (key.getName().equals("IPlacesInfo"))
return CityPlugin.getInstance().getPlacesAdapter();
return null;
}
}
熟悉 Eclipse IDE 工作台的开发人员都了解 Outline 视图,这个视图提供了编辑器中打开的文件的结构化视图。这个 Outline 视图展示了 IAdaptable 接口如何与某些事件类型结合使用,从而有效地根据其他视图的内容对视图进行初始化。编辑器必须为用户打开的文件创建一个 Content Outline 页面。Content Outline 页面符合 IContentOutlinePage 接口。编辑器还必须实现 IAdaptable 接口,这样就能够向编辑器查询 IContentOutlinePage 类型的适配器。使用这个适配器来获取和显示文件的大纲信息。
IAdaptable 接口的另一个例子是 Properties 视图。Properties 视图跟踪对活动部分的选择,并调用当前选择对象上的 getAdapter 方法。查询的适配器类型是 IPropertySource。然后,Properties 视图使用 IPropertySource 适配器来获取要显示的信息。
在这些视图链接例子中,应用程序在接到 Selection Changed 或 Part Activation 通知时,通过 IAdaptable 获取信息。因此,如果选择对象实现了 IAdaptable,那么与从选择对象本身获取的信息量相比,用户可以通过适配器获得多得多的信息。
属性改变监听器范型
可以使用属性改变监听器类型的交互来解决前面提到的另外两个问题:视图如何使用来自未提供视图的插件的信息,以及视图如何公布在可视化选择之后某些处理所生成的信息?
可以建立一个插件来接受属性改变监听器的注册,并在需要时通知注册的监听器。应用程序可以将定制的事件告之监听器,事件中还可以包含要共享的信息。
与选择提供器的情况不同,属性改变提供器不需要实现特定的接口。您必须决定将监听器注册到提供器的语义。清单 4 中的代码片段是一些方法,它们允许在属性提供器视图或插件类中添加或删除属性改变监听器。
清单 4. 添加和删除属性改变监听器
//To add a listener for property changes to this notifier:
public void addPropertyChangeListener(IPropertyChangeListener listener);
//To remove the given content change listener from this notifier:
public void removePropertyChangeListener(IPropertyChangeListener listener);
属性提供器应该使用 org.eclipse.jface.util.PropertyChangeEvent 来创建一个可以有效填充和传播的事件。另外,属性提供器要负责维护监听器列表并对它们进行回调。
作为一个例子,请考虑一个每小时调用 World Weather Web Service 来查询主要城市的气象的插件,它要使这些信息可供其他插件和视图使用。CityWeatherPlugin 可以公开一个称为 CitiesWeatherXML 的属性,消费者可以将本身作为 PropertyChange 监听器注册到 CityWeatherPlugin。为此,监听器必须了解 CityWeatherPlugin 中的注册方法,这样才能将本身注册为气象数据事件的监听器。CityWeatherPlugin 应该跟踪并通知监听器。它使用 PropertyChangeEvent 向监听器提供数据。
清单 5. 创建属性提供器
class CityPopulationPlugin {
ArrayList myListeners;
// A public method that allows listener registration
public void addPropertyChangeListener(IPropertyChangeListener listener) {
if(!myListeners.contains(listener))
myListeners.add(listener);
}
// A public method that allows listener registration
public void removePropertyChangeListener(IPropertyChangeListener listener) {
myListeners.remove(listener);
}
public CityPopulationPlugin (){
// method to start the thread that invokes the population \
web service once every hour
// and then notifies the listeners via the propertyChange() callback method.
initWebServiceInvokerThread( myListeners );
}
void initWebServiceInvokerThread(ArrayList listeners) {
// Code to Invoke Web Service Periodically, and retrieve information
// Post Invocation, inform listeners
for (Iterator iter = listeners.iterator(); iter.hasNext();) {
IPropertyChangeListener element = (IProperty\
ChangeListener) iter.next();
element.propertyChange(new PropertyChangeEvent(this, \"CitiesWeatherXML" , null , CityWeatherXMLObj));
}
}
}
属性改变监听器必须实现 org.eclipse.jface.util.IPropertyChangeListener 接口,以便允许属性改变提供器对它进行回调。这个接口有一个方法 public void propertyChange(PropertyChangeEvent event)。
清单 6. 实现 IPropertyChangeListener
class MyView implements IPropertyChangeListener {
public void createPartControl() {
//register with a Known Plugin that sources Population Data
CityPopulationPlugin.getInstance().addPropertyChangeListener(this);
}
public void propertyChange(PropertyChangeEvent event) {
//This view is interested in the Population Counts of the Cities.
//The population data is being sourced by another
plugin in the background.
if( event.getProperty().equals("CitiesWeatherXML")) {
Object val = event.getNewValue();
// do something with val
}
}
}
这种方式的灵活性在于,应用程序可以在需要时通知监听器,并根据各种场景向它们传递信息。传递的信息不必直接与 UI 选择相关;这些信息可以是某些后期处理的结果。另外,它可以与其他后台作业的状态相关,或者是定期从模型中获取的最新信息。例如,City Selector View 可能不只是传播选择的城市,还使用 PropertyChange 范型将当前选择的城市的气象信息异步地传播给其他消费者。
结束语
本文讨论了使视图相互协作和响应的各种方式。如果 UI 选择本身不够,可以使用 IAdaptable 接口加强它们。属性改变监听器也为满足非 UI 场景提供了更大的灵活性。