丰富的 GUI 中的视图可以以各种方式显示信息
从而改善用户体验
非常自然
UI 视图之间是相互依赖的
需要进行交互
Eclipse 简化了链接 UI 视图的工作
并提供了将视图链接应用到非 UI 场景的方式
简介
Eclipse 平台允许使用可插入组件 —— 插件 —— 帮助创建丰富的图形用户界面(graphical user interfaceGUI)应用程序例如插件可以向 GUI 提供视图但是在现实的应用程序中UI 视图不能是孤立的它们需要根据其他视图的状态进行交互和对本身进行更新
一个简单的例子是描述世界各地的主要旅游目的地的 GUI 应用程序这个 GUI 可能有一个 Select City 视图用于显示旅游景点和公共交通信息
图 视图链接的例子
本文介绍在 Eclipse 中结合视图的方式以及如何对其他视图的状态做出响应还讨论链接视图方式在哪些情况下可能比其他方式合适
Eclipse 开发人员可以依赖以下方法对视图进行链接
选择提供器 选择监听器(selection providerselection listener)模式从而让视图对其他视图中的选择做出反应
IAdaptable 接口与某些事件结合使用
属性改变监听器它允许视图将属性改变事件告之已注册的监听器
选择提供器 选择监听器范型
选择提供器 选择监听器模式能够方便地创建对其他视图中的改变做出响应的视图例如当用户点击代表城市名的 UI 项时另一个视图可能需要显示这个城市的景点详情这样的视图可以使用 UI 选择对象(可能是代表城市名的字符串对象)中包含的信息并使用它从模型中获取和显示其他信息
视图应该能够识别并利用 UI 选择事件orgeclipseuiISelectionListener 是接收 UI 选择事件的监听器接口选择监听器必须注册到工作台页面工作台页面实现 orgeclipseuiISelectionService 接口定义的服务从而将 UI 选择事件告之监听器选择监听器必须注册到选择服务
用于显示可选择的 UI 项的视图还应该能够公布 UI 选择视图通过将 选择提供器 注册到它们各自的工作台站点来实现这一点Eclipse 中的每个 UI 部分通过 orgeclipseuiIWorkbenchPartSite 引用与工作台站点联络选择提供器注册到工作台站点
在使用选择提供器 选择监听器模式链接视图时视图可以将本身作为监听器添加到工作台页面而希望公布选择的其他视图必须将选择提供器添加到它们各自的工作台站点orgeclipseuiISelectionListener 接口如下所示
public void selectionChanged(IWorkbenchPart part ISelection selection);
要使视图能够监听选择改变视图必须实现 ISelectionListener 接口并必须将自己注册到工作台页面清单 显示一个例子
清单 将选择监听器添加到工作台页面
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);
这种方式可以避免对消费者视图进行多余的回调如果视图被注册为非特定的监听器就会出现这种情况 清单 中的代码片段显示一个视图的 createPartControl() 方法这个方法创建一个 JFace TableViewer 并将它作为选择提供器添加到工作台站点这些代码使 TableViewer 中的任何 UI 选择改变能够传播到页面并最终传播到对这种事件感兴趣的消费者视图
清单 设置选择提供器
public void createPartControl(Composite parent) {
// Set up a JFace Viewer
viewer = new TableViewer(parent SWTMULTI | SWTH_SCROLL | SWTV_SCROLL);
viewersetContentProvider(new ViewContentProvider());
viewersetLabelProvider(new ViewLabelProvider());
viewersetSorter(new NameSorter());
viewersetInput(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 选择的链接是不可能的
可以使用 reruntimeIAdaptable 接口来缓解第一个问题这个接口使选择对象能够在需要时传播更多信息第二个和第三个问题需要用手工方式解决属性改变监听器模式是合适的解决方案
使用 IAdaptable 接口
实现 IAdaptable 接口的类能够动态地返回某些类型的适配器然后可以使用这些适配器获取更多信息如果查看器中的选择对象实现了 IAdaptable 接口那么根据它们可以返回的适配器类型可以有效地获取更多信息或相关信息reruntimeIAdaptable 接口如下所示
public void object getAdapter(Class adapter);
显然调用者应该知道它期望选择返回的适配器接口类型考虑一个 JFace TreeViewer它在一个单层的树中显示城市代表城市的对象是 CityClass 类型的CityClass 对象应该包含关于此城市的基本信息并只在需要时返回详细信息在清单 中要注意CityClass 支持的适配器类型使调用者能够在需要时获得更多信息
清单 JFace TreeViewer 中的 CityClass
class CityClass implements IAdaptable {
private String cityName;
public CityClass(String name) {
thisname = name;
}
public String getName() {
return name;
}
public CityClass getParent() {
return parent;
}
public String toString() {
return getName();
}
public Object getAdapter(Class key) {
if (keygetName()equals(ITransportationInfo))
return CityPlugingetInstance()getTransportAdapter();
else (keygetName()equals(IPlacesInfo))
return CityPlugingetInstance()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那么与从选择对象本身获取的信息量相比用户可以通过适配器获得多得多的信息
属性改变监听器范型
可以使用属性改变监听器类型的交互来解决前面提到的另外两个问题视图如何使用来自未提供视图的插件的信息以及视图如何公布在可视化选择之后某些处理所生成的信息?
可以建立一个插件来接受属性改变监听器的注册并在需要时通知注册的监听器应用程序可以将定制的事件告之监听器事件中还可以包含要共享的信息
与选择提供器的情况不同属性改变提供器不需要实现特定的接口您必须决定将监听器注册到提供器的语义清单 中的代码片段是一些方法它们允许在属性提供器视图或插件类中添加或删除属性改变监听器
清单 添加和删除属性改变监听器
//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);
属性提供器应该使用 orgeclipsejfaceutilPropertyChangeEvent 来创建一个可以有效填充和传播的事件另外属性提供器要负责维护监听器列表并对它们进行回调
作为一个例子请考虑一个每小时调用 World Weather Web Service 来查询主要城市的气象的插件它要使这些信息可供其他插件和视图使用CityWeatherPlugin 可以公开一个称为 CitiesWeatherXML 的属性消费者可以将本身作为 PropertyChange 监听器注册到 CityWeatherPlugin为此监听器必须了解 CityWeatherPlugin 中的注册方法这样才能将本身注册为气象数据事件的监听器CityWeatherPlugin 应该跟蹤并通知监听器它使用 PropertyChangeEvent 向监听器提供数据
清单 创建属性提供器
class CityPopulationPlugin {
ArrayList myListeners;
// A public method that allows listener registration
public void addPropertyChangeListener(IPropertyChangeListener listener) {
if(!ntains(listener))
myListenersadd(listener);
}
// A public method that allows listener registration
public void removePropertyChangeListener(IPropertyChangeListener listener) {
myListenersremove(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 = erator(); iterhasNext();) {
IPropertyChangeListener element = (IProperty\
ChangeListener) iternext();
elementpropertyChange(new PropertyChangeEvent(this \CitiesWeatherXML null CityWeatherXMLObj));
}
}
}
属性改变监听器必须实现 orgeclipsejfaceutilIPropertyChangeListener 接口以便允许属性改变提供器对它进行回调这个接口有一个方法 public void propertyChange(PropertyChangeEvent event)
清单 实现 IPropertyChangeListener
class MyView implements IPropertyChangeListener {
public void createPartControl() {
//register with a Known Plugin that sources Population Data
CityPopulationPlugingetInstance()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( eventgetProperty()equals(CitiesWeatherXML)) {
Object val = eventgetNewValue();
// do something with val
}
}
}
这种方式的灵活性在于应用程序可以在需要时通知监听器并根据各种场景向它们传递信息传递的信息不必直接与 UI 选择相关这些信息可以是某些后期处理的结果另外它可以与其他后台作业的状态相关或者是定期从模型中获取的最新信息例如City Selector View 可能不只是传播选择的城市还使用 PropertyChange 范型将当前选择的城市的气象信息异步地传播给其他消费者
结束语
本文讨论了使视图相互协作和响应的各种方式如果 UI 选择本身不够可以使用 IAdaptable 接口加强它们属性改变监听器也为满足非 UI 场景提供了更大的灵活性
共页