前文回顧:
1 插件學習篇
4 SWT編程須知
7 SWT布局詳解
9 編輯器代碼着色
10 JFace開發
事件的監聽,是插件開發中的重要環節,每一次的點擊或者按鍵都有可能觸發某種事件的響應,那么是如何實現的呢?
對於某種被監聽模型,通常需要添加一個監聽隊列。
監聽者需要通過某種方式,加入到這個監聽隊列中。
當這個模型在特定的情況下觸發監聽事件后,會產生一個事件的響應,這個響應使得監聽隊列中的每個監聽者都觸發響應的操作。
例如下面這個小例子:
class FocusedCountry{ List<IListener> listener = new ArrayList(); public void addListener(IListener lis){ listener.add(lis); } //移除監聽者 public void removeListener(IListener lis){ listener.remove(lis); } //觸發監聽事件 protected void fireChange(String message){ for(IListener lis : listener){ lis.noticedChange(message); } } }
這個被監聽的對象,有一個監聽隊列,所有對它感興趣的人都會加入到這個監聽隊列中。因此主要有三個函數,加入到隊列中,從隊列離開,以及本身的一個觸發函數。
interface IListener{ public void noticedChange(String message); } class DevelopedCountry implements IListener{ public void noticedChange(String message) { System.out.println("noticed the change:"+message); } }
上面實現了一個監聽的接口,只要實現了這個接口的類,都可以添加到隊列中。
public class ListenTest { public static void main(String[] args) { DevelopedCountry America = new DevelopedCountry(); FocusedCountry China = new FocusedCountry(); FocusedCountry NorthKorea = new FocusedCountry(); China.addListener(America); NorthKorea.addListener(America); China.fireChange("登月!"); NorthKorea.fireChange("原子彈造好了,該去哪扔呢!"); } }
調用結果如下,所有的事件都被監聽者接收到了。
noticed the change:登月!
noticed the change:原子彈造好了,該去哪扔呢!
那么GEF中是如何使用的呢?
GEF是一種MVC標准的架構,它的模型負責實現這個監聽隊列,而Control負責接收監聽,進行響應,從而改變View的模型。
因此,一般的Model都會繼承一個自定義的虛類,這個虛類中包含了一個監聽隊列,以及上面提到的三種函數。
public class AbstractModel implements Serializable{ private PropertyChangeSupport listeners = new PropertyChangeSupport(this); public void addPropertyChangeListener(PropertyChangeListener listener) { listeners.addPropertyChangeListener(listener); } public void firePropertyChange(String propName, Object oldValue,Object newValue) { listeners.firePropertyChange(propName, oldValue, newValue); } public void removePropertyChangeListener(PropertyChangeListener listener) { listeners.removePropertyChangeListener(listener); } }
繼承這個類后,需要某些事件進行觸發監聽,一般情況下,模型都會對應一些屬性視圖,屬性視圖需要繼承IPropertySource接口。並重寫下面的方法。
public IPropertyDescriptor[] getPropertyDescriptors() { return new IPropertyDescriptor[] { new PropertyDescriptor(P_TABLE_NAME, "table_name"), } public Object getPropertyValue(Object id) { if (id == P_TABLE_NAME) { return getPhysicalName(); } return null; } public boolean isPropertySet(Object id) { if (id == P_TABLE_NAME) { return true; } return false; } public void setPropertyValue(Object id, Object value) { if (id == P_TABLE_NAME) { seName((String) value); } }
屬性視圖上的屬性發生改變時,一般是在Set值的時候會觸發這個firechange,最后觸發到listners里面的firePropertyChange函數。
public void setXXXlName(String xxxName) { this.xxxName = xxxName; firePropertyChange(P_XXX_NAME, null, xxxName); }
這里是一個插件開發遺留的習慣,就是會把每一個事件使用一個static的字符串進行標記。函數會產生一個PropertyChange的事件。
這樣模型部分的監聽就搞定了,下面要進行的是監聽者的添加了。
這里監聽者需要實現PropertyChangeListener接口,並在適合的時機添加到監聽隊列中,由於這部分的代碼在Editpart中,GEF的每一個Editpart都對應了一個Model,因此通過簡單的getModel方法就可以獲取它對應的模型對象,再調用模型對象的addListener等方法添加到監聽隊列中就OK了。
public void activate() { if (isActive()) { return; } super.activate(); ((TableModel) getModel()).addPropertyChangeListener(this); } public void deactive() { if (!isActive()) { return; } super.deactivate(); ((TableModel) getModel()).removePropertyChangeListener(this); }
一般來說都是在這兩個函數內,因為這兩個函數相當於處於 一般函數的 構造函數 和 析構函數的 執行位置。
添加完監聽隊列,需要實現一下PropertyChangeListener里面的PropertyChange方法,這個方法傳遞一個參數,通過這個參數可以獲取上面最開始設定的字符串,從而判斷是模型的哪個時間發生了響應。
public void propertyChange(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(TableModel.P_TABLE_NAME)) refreshVisuals(); ... }