JavaFX事件(1)EventHandler處理架構 by 黃嘉輝 | CodeData
top

JavaFX事件(1)EventHandler處理架構

分享:

JavaFX多媒體(4)Equalizer 與 Audio Spectrum << 前情

欲建立JavaFX程式與使用者間的互動,需處理物件的事件,所謂事件 (Event) 是指當物件其狀態改變時所觸發產生的相關動作,例如按下滑鼠按鍵、滑鼠拖曳、按下鍵盤按鍵等,均會觸發其相對應的事件。

AWT/Swing事件處理架構

在Java AWT與Java Swing中,處理事件的方式有兩種,第一種方式為實作處理事件的Listener介面,各Listener介面提供處理事件的方法。其次為處理事件的類別,並分為Event類別與Adapter抽象類別,以定義物件所產生的事件。

後續 >> JavaFX事件(2)按鍵事件

以實作Listener介面為例,程式需實作介面所提供的全部方法,不論是否使用到,均需全部實作於程式中,若需處理某一事件,則將Listener介面所提供的方法內容加以覆寫 (Override)。此外,Listener介面需以下列方法處理:

  • addXXXListener():註冊事件的Listener,以處理物件所產生的相關事件。
  • getXXXListeners():取得物件所註冊的Listener,並以陣列回傳所註冊的所有相關事件。
  • removeXXXListener():移除物件所註冊的事件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) 的事件類別:

  • ActionEvent:動作事件。
  • ContextMenuEvent:快顯選單事件。
  • DragEvent:拖曳事件,適用於滑鼠與觸控裝置。
  • GestureEvent:手勢事件,適用於觸控裝置。
  • InputEvent:輸入事件。
  • InputMethodEvent:輸入方法事件。
  • KeyEvent:按鍵事件。
  • MediaErrorEvent:多媒體錯誤事件。
  • MouseEvent:滑鼠事件。
  • MouseDragEvent:滑鼠拖曳事件,有別於DragEvent
  • RotateEvent:旋轉事件,適用於觸控裝置。
  • ScrollEvent:捲動事件,適用於滑鼠與觸控裝置。
  • SwipeEvent:滑動事件,適用於觸控裝置。
  • TouchEvent:觸控事件,適用於觸控裝置。
  • WebEvent:Web Engine事件。
  • WindowEvent:視窗事件。
  • WorkerStateEvent:Worker狀態改變時之事件。
  • ZoomEvent:縮放事件,適用於觸控裝置。

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
[2] JavaFX
[3] NetBeans
[4] JavaFX 8.0 API Specification.
[5] Java Platform, Standard Edition 8 API Specification.

 

分享:
按讚!加入 CodeData Facebook 粉絲群

留言

留言請先。還沒帳號註冊也可以使用FacebookGoogle+登錄留言

關於作者

黃嘉輝副教授,目前任職於國立臺北商業大學企業管理學系,喜歡寫程式,特別愛Java,範例可參考教學網站

熱門論壇文章

熱門技術文章