Java Swing中的事件機制和觀察者模式
Java Swing簡述
Java Swing是Java 提供的一套關于使用者圖形界面(GUI)程式設計的工具包,如今已是Java基礎類的一部分。其中包含圖形化界面程式設計經常使用的要素,例如:容器、元件(文本框、按鈕、下拉菜單、表等)、布局等等。相比與傳統的Java AWT,Java Swing擁有更好的平台移植性。java
做為一款GUI程式設計工具包,Java Swing或許如今遠不如QT、MFC、HTML5等技術流行,可是在其中所包含的事件處理倒是幾乎全部GUI程式設計中所共有的技術。由于GUI工具包不隻須要提供優美的圖形化元件,同時還須要可以與使用者進行互動,而互動的核心就是事件處理程式設計
事件處理機制
事件處理機制是一種事件處理架構,其設計目的是把GUI互動動做(單擊、菜單選擇等)轉變為調用相關的事件處理程式進行處理。JDK 1.1之後Java采起了受權處理機制(Delegation—based Model),事件源能夠把在其自身全部可能發生的事件分别受權給不一樣的事件處理者來處理。設計模式
------百度百科架構
事件機制的三個基本要素
事件(event) 一般是指某種類型的操做。例如單擊了一次Button或者文本框中輸入了一個字元,則單擊、輸入字元就是事件ide
事件源(Event Source):能夠簡單的将其了解為事件發生的源頭,例如單擊了一次Button,因為單擊是在Button上發生的,是以Button就是事件發生源頭,即事件源。在Swing中,一般包含了全部能互動的的元件函數
事件監聽器(Listener):是事件處理機制的核心,定義了全部事件處理的相關邏輯。事件監聽器的主要做用,是關注可能發生事件的對象(事件源),并在其發生了特定的事件後,能對其作出反應工具
事件監聽機制與觀察者模式之間的聯系
從事件監聽器(Listener)的做用上分析,監聽器的功能就是可以感覺到對象上發生了某個事件。換句話說,就是當事件源上發生了某個事件時,監聽器但願能被通知到,而且在獲得通知後能對其作出相應的處理。而這實際上就是觀察者模式中觀察者所但願的事情。是以,咱們能夠很天然将監聽者映射到觀察者模式中觀察者(Observer),那麼事件源和事件就相應的映射到了觀察者模式中的目标(Subject)。布局
下面就從源碼上驗證上述結論:this
Subject的一些必要條件lua
知道他的觀察者。(維護觀察者清單)
提供注冊和删除觀察者的接口
當狀态發生變化時,向觀察者發送通知
Observer的一些必要條件
當目标發生變化時,提供一個更新的接口(回調函數)
Swing中的元件類(等價于觀察者中的Subject)
元件類在監聽機制中就是事件源
(因為代碼過長,隻截取重要部分)
全部元件的父類JComponent
public abstract class JComponent extends Container implementsSerializable,
TransferHandler.HasGetTransferHandler
{
...
//維護了觀察者的清單
protected EventListenerList listenerList = newEventListenerList();
...
}
View Code
該類中定義了,全部元件類所須要維護的監聽器清單
某個相對具體元件AbstractButton
public abstract class AbstractButton extends JComponent implementsItemSelectable, SwingConstants {
...//對于觀察者的管理 start// public voidaddActionListener(ActionListener l) {
listenerList.add(ActionListener.class, l);
}public voidremoveActionListener(ActionListener l) {if ((l != null) && (getAction() ==l)) {
setAction(null);
}else{
listenerList.remove(ActionListener.class, l);
}
}
publicActionListener[] getActionListeners() {return listenerList.getListeners(ActionListener.class);
}//對于觀察者的管理 end//
//向觀察者發送通知 start// protected voidfireActionPerformed(ActionEvent event) {//Guaranteed to return a non-null array
Object[] listeners =listenerList.getListenerList();
ActionEvent e= null;//Process the listeners last to first, notifying//those that are interested in this event
for (int i = listeners.length-2; i>=0; i-=2) {if (listeners[i]==ActionListener.class) {//Lazily create the event:
if (e == null) {
String actionCommand=event.getActionCommand();if(actionCommand == null) {
actionCommand=getActionCommand();
}
e= new ActionEvent(AbstractButton.this,
ActionEvent.ACTION_PERFORMED,
actionCommand,
event.getWhen(),
event.getModifiers());
}
((ActionListener)listeners[i+1]).actionPerformed(e);
}
}
}//向觀察者發送通知 end//...
}
View Code
Swing中的監聽器類(等價于觀察者中的Observer)
(因為代碼過長,隻截取重要部分)
監聽器類就是事件機制中的事件監聽器
全部監聽器的父類EventListener
public interfaceEventListener {
}
View Code
該類并未定義任何方法,實際上他的做用隻是一個标記類,用于身份的說明;
某個相對具體監聽器ActionListener
public interface ActionListener extendsEventListener {public voidactionPerformed(ActionEvent e);
}
View Code
目前為止,我已經簡單地闡述了事件監聽機制中【事件源(Event Source)、監聽者(Listener)】和觀察者模式中【目标(Subject),觀察者(Observer)】之間的映射關系。彷佛還少了一個重要地角色事件(event)。下面貼出event的源碼
Swing中的事件類
全部事件類的父類EventObject
public class EventObject implementsjava.io.Serializable {
...protected transientObject source;publicObject getSource() {returnsource;
}
...
}
View Code
某個具體的事件類
public class ActionEvent extendsAWTEvent {
...
String actionCommand;intmodifiers;publicString getActionCommand() {returnactionCommand;
}public longgetWhen() {returnwhen;
}
...
}
View Code
僅從代碼上看,event隻是在事件源上作了一層封裝,同時儲存一些事件的狀态資訊,而後回傳給了監聽器的回調函數,這樣監聽器中的更新方法就能夠得到了事件源,以及事件相關的資訊。從觀察者的角度上說,就是觀察者得到了目标的相關狀态。是以,在這裡我能夠将事件監聽機制中(事件源和事件)二者同時映射到觀察者模式中的目标(Subject)上
實際上event作的事情不止這些,他是事件發起者和事件接收者之間的橋梁。從觀察者模式的角度上說,就是觸發者與觀察者之間的橋梁。(觸發者:改變目标(subject)狀态的對象)。
在事件監聽機制中,監聽器通常不會主動去觸發一些事件(這一點與觀察者不一樣,在觀察者設計模式中,觀察者能夠直接改變目标的狀态)。事件每每是由系統觸發的,例如點選事件,敲擊鍵盤的事件等等。監聽者每每做為一個被動通知的對象。系統會實時捕捉一些事件,而後将其放到一個事件隊列當中,而後Swing會單獨開啟一個線程,從事件隊列中得到事件,而後對事件進行分發,最終經過事件中維護的事件源通知監聽者。
java swing中的源碼:
事件分發的線程:EventDispatchThread
class EventDispatchThread extendsThread {
...public voidrun() {try{
pumpEvents(newConditional() {public booleanevaluate() {return true;
}
});
}finally{
getEventQueue().detachDispatchThread(this);
}
}
...
}
View Code
從pumpEvents一路追源碼:
class EventDispatchThread extendsThread {void pumpOneEventForFilters(intid) {
...
eq.dispatchEvent(event);
...
}
}public classEventQueue {
...privateEventDispatchThread dispatchThread;//事件分發
protected void dispatchEvent(finalAWTEvent event) {
...
dispatchEventImpl(event, src);
...
}
View Code
從dispatchEventImpl繼續一路追:
private void dispatchEventImpl(final AWTEvent event, finalObject src) {
event.isPosted= true;
...if (event instanceofActiveEvent) {//This could become the sole method of dispatching in time.
setCurrentEventAndMostRecentTimeImpl(event);
((ActiveEvent)event).dispatch();
}else if (src instanceofComponent) {
((Component)src).dispatchEvent(event);
event.dispatched();
}
...
}
View Code
最後得到Component 對象,再往下就是得到Listhenerlist,而後逐個調用
綜合所述:Swing的事件分發線程 從事件隊列中得到事件,而後進行事件分發。在分發過程當中經過事件得到事件源,在經過事件源通知到相應的監聽器。大體關系以下:事件分發線程 -> 事件-> 事件源->監聽器
觀察者模式的總結
1)觀察者模式是一種反向通知的機制,觀察者須要知道目标(Subject)的狀态是否發生了變化。可是感覺目标(Subject)狀态變化,并非由觀察者不斷地去詢問來實作,而是利用回調函數地方法,将觀察者注冊到目标中去,經過目标(Subject)自身地notify的方法反向通知觀察者。相比與觀察者主動詢問,回調的方式有更好的即時性。
2)自然地支援廣播通訊,目标(Subject)不須要指定它的接收者,而是簡單地将資訊發送出去,由觀察者決定是否對通訊進行處理。
3)将目标(Subject)和觀察者進行分離/解耦: 一方面目标(Subject)不須要知道觀察者,它所要作地僅僅是将本身的變化,經過觀察者提供的方法,發送給觀察者。不須要在乎觀察者的邏輯。而觀察者也不須要知道目标(Subject)的實作,它隻須要在乎當變化發生時,本身須要處理的邏輯。