JavaFX事件(1)EventHandler處理架構
JavaFX多媒體(4)Equalizer 與 Audio Spectrum << 前情 欲建立JavaFX程式與使用者間的互動,需處理物件的事件,所謂事件 (Event) 是指當物件其狀態改變時所觸發產生的相關動作,例如按下滑鼠按鍵、滑鼠拖曳、按下鍵盤按鍵等,均會觸發其相對應的事件。 AWT/Swing事件處理架構在Java AWT與Java Swing中,處理事件的方式有兩種,第一種方式為實作處理事件的Listener介面,各Listener介面提供處理事件的方法。其次為處理事件的類別,並分為Event類別與Adapter抽象類別,以定義物件所產生的事件。 以實作Listener介面為例,程式需實作介面所提供的全部方法,不論是否使用到,均需全部實作於程式中,若需處理某一事件,則將Listener介面所提供的方法內容加以覆寫 (Override)。此外,Listener介面需以下列方法處理:
以下為以JFrame類別實作MouseListener介面以處理滑鼠事件的程式架構: import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; // 實作MouseListener介面 public class JavaEventDemo extends javax.swing.JFrame implements MouseListener { // 建構函式 public JavaEventDemo() { ... // 註冊MouseListener this.addMouseListener(this); } // 實作MouseListener介面的方法 public void mouseClicked(MouseEvent e) {...} public void mouseEntered(MouseEvent e) {...} public void mouseExited(MouseEvent e) {...} public void mousePressed(MouseEvent e) {...} public void mouseReleased(MouseEvent e) {...} ... } 在實作Listener介面時,即使未使用到該事件的方法,仍需將介面所提供的方法全部描述一遍,並將方法的內容以空白表示,欲處理事件則覆寫該介面的方法。 除了實作介面之外,另一種處理事件的方法為事件類別,並分為Event類別與Adapter抽象類別,以定義不同物件所產生的事件。Event類別繼承自java.util.EventObject類別,並對應於Listener介面,例如以MouseListener介面為例,其Event類別則為MouseEvent。 其次,Adapter抽象類別為Listener介面相對應的類別,同樣提供與Listener介面一樣的方法,但不同的是,使用上是以繼承Adapter抽象類別,但僅處理所發生的方法,而未發生的方法則不需重新描述一遍,因此可精簡程式。以下是以Inner Class的方式使用Adapter類別: import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; public class JavaEventDemo extends javax.swing.JFrame // 建構函式 public JavaEventDemo() { ... // 以Inner Class的方式使用滑鼠的Adapter類別 this.addMouseListener(new MouseInputAdapter() { // 處理滑鼠事件 public void mousePressed(MouseEvent e) { ... } }); } } 以下為繼承Adapter抽象類別的程式架構: import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; public class JavaEventDemo extends javax.swing.JFrame // 建構函式 public JavaEventDemo() { ... // 以自訂MouseAdapter類別建立MouseListener this.addMouseListener(new MyMouseAdapter()); ... } } // 繼承MouseAdapter class MyMouseAdapter extends MouseInputAdapter { // 建構函式 public MyMouseAdapter() { ... } // 處理滑鼠事件 public void mousePressed(MouseEvent e) { ... } } JavaFX事件處理架構JavaFX簡化處理事件的方式,不像AWT/Swing以不同的Listener介面處理各類事件,JavaFX僅以javafx.event.EventHandler介面處理各類事件,且介面僅提供handle()方法。 至於如何以單一的EventHandler介面處理各類事件呢?這點可說是JavaFX的一項重大改變。 JavaFX以事件類型 (Event Type) 定義各類事件,其基礎類別為javafx.event.Event,繼承自Event類別的事件類別分別如下所示,其中較特殊的是手勢與觸控事件,由於目前大部份裝置均支援觸控螢幕 (Touch Screen) 與觸控板 (Touch Pad),因此JavaFX增加處理多點觸控 (Multi-Touch) 的事件類別:
JavaFX處理事件的第一種方式是以物件的setOnXXX()方法設定處理事件的Event Handler函式,其語法如下,其中[Event_TYPE]為上述之事件類別: [object].setOnXXX(new EventHandler<[Event_TYPE]>() { @Override public void handle([Event_TYPE] e) { ... } }); 以按鈕的動作事件為例,其設定Event Handler函式的方法為setOnAction()、事件類別為ActionEvent: Button button = new Button(); button.setOnAction(new EventHandler() { @Override public void handle(ActionEvent e) { ... } }); 以按鈕的滑鼠移動事件為例,其設定方法為setOnMouseMoved()、事件類別為MouseEvent: Button button = new Button(); button.setOnMouseMoved(new EventHandler() { @Override public void handle(MouseEvent e) { ... } }); 由上述說明可以瞭解,JavaFX以單一EventHandler介面與不同事件類別的組合處理事件,相較於Java以實作處理事件的Listener介面、或使用Event類別與Adapter抽象類別,JavaFX處理事件的方式更為簡單。 除了上述語法之外,亦可以建立類別的方式處理,以按鈕的動作事件為例: Button button = new Button(); button.setOnAction(onActionEventHandler); ... EventHandler onActionEventHandler = new EventHandler() { @Override public void handle(ActionEvent e) { ... } }; 除了以setOnXXX()方法設定處理事件的Event Handler函式之外,JavaFX處理事件的第二種方式是以物件的addEventHandler()方法註冊事件的Event Handler,其語法如下,其中[eventType]代表事件類型、[Event_TYPE]為前述之事件類別: [object].addEventHandler([eventType], new EventHandler<[Event_TYPE]>() { @Override public void handle([Event_TYPE] e) { ... } }); 以按鈕的動作事件為例,其事件類型為ActionEvent.ACTION、事件類別為ActionEvent,註冊Event Handler的程式架構如下: Button button = new Button(); button.addEventHandler(ActionEvent.ACTION, new EventHandler() { @Override public void handle(ActionEvent e) { ... } } ); 除了上述語法之外,亦可以建立類別的方式處理,以按鈕的動作事件為例: Button button = new Button(); button.addEventHandler(ActionEvent.ACTION, onActionEventHandler); ... EventHandler onActionEventHandler = new EventHandler() { @Override public void handle(ActionEvent e) { ... } }; JavaFX處理事件的第三種方式是以物件的addEventFilter()方法註冊事件的Event Filter,其語法如下,與Event Handler幾乎一樣,其中[eventType]代表事件類型、[Event_TYPE]為前述之事件類別: [object].addEventFilter([eventType], new EventHandler<[Event_TYPE]>() { @Override public void handle([Event_TYPE] e) { ... } }); 以按鈕的動作事件為例,其事件類型為ActionEvent.ACTION、事件類別為ActionEvent,註冊Event Filter的程式架構如下: Button button = new Button(); button.addEventFilter(ActionEvent.ACTION, new EventHandler() { @Override public void handle(ActionEvent e) { ... } } ); 除了上述語法之外,亦可以建立類別的方式處理,以按鈕的動作事件為例: Button button = new Button(); button.addEventFilter(ActionEvent.ACTION, onActionEventFilter); ... EventHandler onActionEventFilter = new EventHandler() { @Override public void handle(ActionEvent e) { ... } }; 此外Java 8.0新增Lambda Expression,其語法如下: (argument) -> (body) Lambda Expression是一種匿名函數 (Anonymous Function),沒有Method Declaration (方法宣告),亦不需要Modifier (修飾詞) 與Return Value Declaration (回傳值宣告) 等,因此程式更為精簡。以下是一些簡單的例子: (int a, int b) -> {return a + b;} () -> System.out.println("..."); (String value) -> {return value;} 我們可以將Lambda Expression運用於JavaFX的事件處理上,讓處理事件的方式更為簡單。透過Lambda Expression可將上述程式精簡如下,省略EventHandler介面的描述: Button button = new Button(); button.setOnMouseMoved((ActionEvent e) -> { ... }); 或以建立類別的方式處理: Button button = new Button(); button.setOnAction(onActionEventHandler); ... EventHandler onActionEventHandler = (EventHandler<ActionEvent>)(ActionEvent e) -> { ... }; 更進一步可依賴編譯器的型態推斷 (Type Inference),讓程式更精簡: Button button = new Button(); button.setOnAction(event -> { ... }); 【參考資料】 [1] Java Official Web Site
|