程式|連結|視圖 豐富的 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 情境提供了更大的靈活性。