前文回顧:html
1 插件學習篇編程
3 利用擴展點,開發透視圖編輯器
4 SWT編程須知函數
5 SWT簡單控件的使用與佈局搭配佈局
6 SWT複雜空間與佈局搭配學習
7 SWT佈局詳解this
8 IPreferenceStore使用詳解spa
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是一種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(); ... }