RCP 視圖交互間消息機制 selection provider-selection listener
Posted on 2012-07-05 16:14 小胡子 閱讀(1314) 評論(2) 編輯 收藏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. 將選擇監聽器添加到工作臺頁面
1 public class MyView extends ViewPart implements ISelectionListener { 2 public void createPartControl(Composite parent) { 3 // add this view as a selection listener to the workbench page 4 getSite().getPage().addSelectionListener((ISelectionListener) this); 5 } 6 7 // Implement the method defined in ISelectionListener, to consume UI 8 // selections 9 public void selectionChanged(IWorkbenchPart part, ISelection selection) { 10 // Examine selection and act on it! 11 } 12 } |
使用 UI 選擇的更好的方法是,將消費者視圖作為監聽器注冊到特定的視圖部分。正如在下面的例子中可以看到的,源視圖部分的視圖 ID 在注冊選擇監聽器期間被作為一個參數。
這種方式可以避免對消費者視圖進行多余的回調,如果視圖被注冊為非特定的監聽器,就會出現這種情況。
清單 2 中的代碼片段顯示一個視圖的 createPartControl()
方法,這個方法創建一個 JFace TableViewer 并將它作為選擇提供器添加到工作臺站點。這些代碼使 TableViewer 中的任何 UI 選擇改變能夠傳播到頁面,并最終傳播到對這種事件感興趣的消費者視圖。
清單 2. 設置選擇提供器
2 // Set up a JFace Viewer
3 viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
4 viewer.setContentProvider(new ViewContentProvider());
5 viewer.setLabelProvider(new ViewLabelProvider());
6 viewer.setSorter(new NameSorter());
7 viewer.setInput(getViewSite());
8 // ADD the JFace Viewer as a Selection Provider to the View site.
9 getSite().setSelectionProvider(viewer);
10 }
這個視圖將它創建的 JFace TableViewer 注冊為選擇提供器有兩個原因:
- 這個視圖打算使用這個 TableViewer 顯示信息,而且用戶將與 TableViewer 進行交互。
- TableViewer 實現了選擇提供器接口并能夠向工作臺部分站點傳播選擇事件。
因為 JFace 查看器是選擇提供器,所以在大多數情況下就不必創建選擇提供器了。視圖只需使用眾多的 JFace 查看器之一來顯示信息,并將 JFace 查看器注冊為選擇提供器。
某些情況需要另一種視圖鏈接方式:
- 信息量可能太大,由于內存使用量增加,UI 選擇對象無法有效地容納它。
- 視圖可能希望公布其他信息,而不只是公布可視化選擇信息。公布的信息可能是根據選擇進行某些后期處理的結果。
- 視圖可能希望使用來自另一個插件的信息,而這個插件可能根本沒有提供視圖(使用包含的 JFace 查看器)。在這種情況下,使用基于 UI 選擇的鏈接是不可能的。
可以使用 org.eclipse.core.runtime.IAdaptable
接口來緩解第一個問題,這個接口使選擇對象能夠在需要時傳播更多信息。第二個和第三個問題需要用手工方式解決,屬性改變監聽器模式是合適的解決方案。
實現 IAdaptable
接口的類能夠動態地返回某些類型的適配器,然后可以使用這些適配器獲取更多信息。如果查看器中的選擇對象實現了 IAdaptable
接口,那么根據它們可以返回的適配器類型,可以有效地獲取更多信息或相關信息。org.eclipse.core.runtime.IAdaptable
接口如下所示。
顯然,調用者應該知道它期望選擇返回的適配器接口類型。考慮一個 JFace TreeViewer,它在一個單層的樹中顯示城市。代表城市的對象是 CityClass
類型的。CityClass
對象應該包含關于此城市的基本信息,并只在需要時返回詳細信息。在清單 3 中要注意,CityClass
支持的適配器類型使調用者能夠在需要時獲得更多信息。
清單 3. JFace TreeViewer 中的 CityClass
2 private String cityName;
3
4 public CityClass(String name) {
5 this.name = name;
6 }
7
8 public String getName() {
9 return name;
10 }
11
12 public CityClass getParent() {
13 return parent;
14 }
15
16 public String toString() {
17 return getName();
18 }
19
20 public Object getAdapter(Class key) {
21 if (key.getName().equals("ITransportationInfo"))
22 return CityPlugin.getInstance().getTransportAdapter();
23 else (key.getName().equals("IPlacesInfo"))
24 return CityPlugin.getInstance().getPlacesAdapter();
25 return null; }
26 }
27
熟悉 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. 添加和刪除屬性改變監聽器
1 //To add a listener for property changes to this notifier: 2 public void addPropertyChangeListener(IPropertyChangeListener listener); 3 //To remove the given content change listener from this notifier: 4 public void removePropertyChangeListener(IPropertyChangeListener listener); |
屬性提供器應該使用 org.eclipse.jface.util.PropertyChangeEvent
來創建一個可以有效填充和傳播的事件。另外,屬性提供器要負責維護監聽器列表并對它們進行回調。
作為一個例子,請考慮一個每小時調用 World Weather Web Service 來查詢主要城市的氣象的插件,它要使這些信息可供其他插件和視圖使用。CityWeatherPlugin 可以公開一個稱為 CitiesWeatherXML 的屬性,消費者可以將本身作為 PropertyChange
監聽器注冊到 CityWeatherPlugin。為此,監聽器必須了解 CityWeatherPlugin 中的注冊方法,這樣才能將本身注冊為氣象數據事件的監聽器。CityWeatherPlugin 應該跟蹤并通知監聽器。它使用 PropertyChangeEvent
向監聽器提供數據。
清單 5. 創建屬性提供器
2 ArrayList myListeners; // A public method that allows listener registration
3
4 public void addPropertyChangeListener(IPropertyChangeListener listener) {
5 if (!myListeners.contains(listener))
6 myListeners.add(listener);
7 }
8
9 // A public method that allows listener registration
10 public void removePropertyChangeListener(IPropertyChangeListener listener) {
11 myListeners.remove(listener);
12 }
13
14 public CityPopulationPlugin() {
15 // method to start the thread that invokes the population \ web service
16 // once every hour
17 // and then notifies the listeners via the propertyChange() callback
18 // method.
19 initWebServiceInvokerThread(myListeners);
20 }
21
22 void initWebServiceInvokerThread(ArrayList listeners) {
23 // Code to Invoke Web Service Periodically, and retrieve information
24 // Post Invocation, inform listeners
25 for (Iterator iter = listeners.iterator(); iter.hasNext();) {
26 IPropertyChangeListener element = (IPropertyChangeListener) iter.next();
27 element.propertyChange(new PropertyChangeEvent(this, "CitiesWeatherXML", null,
28 CityWeatherXMLObj));
29 }
30 }
31 }
屬性改變監聽器必須實現 org.eclipse.jface.util.IPropertyChangeListener
接口,以便允許屬性改變提供器對它進行回調。這個接口有一個方法
清單 6. 實現 IPropertyChangeListener
2 public void createPartControl() {
3 // register with a Known Plugin that sources Population Data
4 CityPopulationPlugin.getInstance().addPropertyChangeListener(this);
5 }
6
7 public void propertyChange(PropertyChangeEvent event) {
8 // This view is interested in the Population Counts of the Cities.
9 // The population data is being sourced by another plugin in the
10 // background.
11 if (event.getProperty().equals("CitiesWeatherXML")) {
12 Object val = event.getNewValue();
13 // do something with val
14 }
15 }
16 }
這種方式的靈活性在于,應用程序可以在需要時通知監聽器,并根據各種場景向它們傳遞信息。傳遞的信息不必直接與 UI 選擇相關;這些信息可以是某些后期處理的結果。另外,它可以與其他后臺作業的狀態相關,或者是定期從模型中獲取的最新信息。例如,City Selector View 可能不只是傳播選擇的城市,還使用 PropertyChange
范型將當前選擇的城市的氣象信息異步地傳播給其他消費者。
本文討論了使視圖相互協作和響應的各種方式。如果 UI 選擇本身不夠,可以使用 IAdaptable
接口加強它們。屬性改變監聽器也為滿足非 UI 場景提供了更大的靈活性。