今回はキー入力やマウス入力、ボタンの押下などユーザ入力を処理するためのイベント処理についてみていく。
■ イベント処理の流れ
イベント処理する場合には、イベントを処理する関数(イベント・ハンドラ、もしくはイベント・フィルタ)をイベント・タイプ別に登録する。登録にはaddEventHandler関数かaddEventFilter関数を用いる。
イベント・タイプ(EventType)とはイベントの種類のことで、例えばキー押下時にはKeyEvent.KEY_PRESSEDタイプのイベントが発生する。イベント・タイプはEventクラスのサブクラスに静的定数フィールドとして定義されている。どのようなイベント・タイプが定義されているかはJavaDoc(Eventクラス)を参照のこと。
■ イベントの種類
JavaFXで処理可能なイベントは『
JavaDoc - クラスEvent』に記述がある。よく利用するイベントは以下の通りである。
イベントクラス |
定数 |
内容 |
ActionEvent |
ANY |
UIコントロール(ボタンなど)の全イベントを示す |
ACTION |
UIコントロール(ボタンなど)のイベントを示す |
KeyEvent |
ANY |
キー入力の全イベントを示す |
KEY_PRESSED |
キーを押した場合のイベントを示す |
KEY_RELEASED |
キーを離した場合のイベントを示す |
KEY_TYPED |
キーがタイプ(押す→離す)された場合のイベントを示す |
CHAR_UNDEFINED |
文字に対応していないキーを押すもしくは離した場合のイベントを示す |
MouseEvent |
ANY |
マウス入力の全イベントを示す |
MOUSE_CLICKED |
マウスをクリックした場合のイベントを示す |
MOUSE_PRESSED |
マウスを押した場合のイベントを示す |
MOUSE_RELEASED |
マウスを離した場合のイベントを示す |
MOUSE_ENTERED |
マウスがオブジェクト上に入った(乗った)場合のイベントを示す |
MOUSE_EXITED |
マウスがオブジェクト上から離れた場合のイベントを示す |
MOUSE_MOVED |
マウスがオブジェクト上で動いた場合のイベントを示す |
DRAG_DETECTED |
オブジェクト上でドラック操作を検知した場合のイベントを示す |
■ イベント・ハンドラ登録メソッド
イベント・ハンドラ(およびイベント・フィルタ)を登録する関数は以下の通り。第1引数に処理対象のイベント・タイプを指定し、第2引数に処理関数(EventHandlerインターフェースを継承したクラス)を指定する。Stage,Scene,SceneGraphに属するクラスであれば、たいてい登録が可能となっている。
例)ボタン(button)押下時のイベント・ハンドラとしてhandler関数を登録
button.
addEventHandler( ActionEvent.ACTION , handler )
例)ボタン(button)押下時のイベント・フィルターとしてhandler関数を登録
button.
addEventFilter( ActionEvent.ACTION , handler )
一度、登録したイベント・ハンドラ(及びイベント・フィルター)については、removeEventHandler関数で登録を解除できる。
■ コンビニエンス・メソッド
よく使われるタイプのイベントについては、コンビニエンス・メソッドと呼ばれるイベント・ハンドラ登録関数が用意されている。コンビニエンス・メソッドではイベント・タイプがあらかじめ指定されているため、引数としてイベント・ハンドラ関数を渡すだけでよい。
例)KEY_TYPEDのイベント・ハンドラを登録する場合
通常のイベントハンドラ登録 |
: button.addEventHandler( KeyEvent.KEY_TYPED , handler ) |
コンビニエンス・メソッドで登録する場合 |
: button.setOnKeyTyped( handler ) |
■ イベント・ハンドラとイベント・フィルターの違い
イベント処理を行う関数は、イベント・ハンドラとイベント・フィルターに分けられる。イベント処理関数を登録する場合において、イベント・ハンドラとして登録する場合にはaddEventHandler関数を、イベント・フィルターとして登録する場合はaddEventFilter関数を利用するよう明確に分けられている。しかし、関数の実装に違いはなく、両者ともEventHandlerインターフェースを実装したクラスである。
では、違いは何かというと、それはイベント処理の順序である。イベント処理は以下の順序で処理される。
- イベントの発生
- イベント発生ノード(ターゲット)を決定
- イベント発生ノードの全ての親をイベント・ルートとする
- イベント・ルート上のノードに対して、イベント・フィルタ関数を呼び出す。
- イベント・ルート上のノードに対して、イベント・ハンドラ関数を呼び出す。
このため、処理順としてはイベント・フィルタが先に処理され、イベント・ハンドラーが後で処理されることになる。関数中でEvent.consume関数が呼び出されると以降の処理を行わなくなるため、イベント・フィルタを利用して、ある場合にはイベント・ハンドラを呼び出さないといった処理が可能となる。
さらにイベント・フィルタ関数の呼び出しは『ルート・ノード→ターゲット・ノード』の順で行われるのに対し、イベント・ハンドラ関数の呼び出しは『ターゲット・ノード→ルート・ノード』の順で行われる点も異なる。
■ サンプル
イベント・フィルタ及びイベント・ハンドラの使い方と動作を確認するためのサンプルプログラムを以下に示す。
◇サンプルプログラム
package application_fx;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
public class TestEvent extends Application {
public static void main(String[] args)
{
launch( args );
}
@Override
public void start(Stage primaryStage) throws Exception
{
// シーングラフの作成
FlowPane root = new FlowPane();
Label label = new Label( "押してね" );
Button btn = new Button( "push me" );
root.getChildren().add( label );
root.getChildren().add( btn );
// シーンの作成
Scene scene = new Scene( root , 200 , 100 );
/* アクションイベント、キーイベントの使い方を確認 */
// ボタンに押下処理を追加する
EventHandler<ActionEvent> btnActionFilter = ( event ) -> { System.out.println( "button push!" ); event.consume(); };
btn.addEventHandler( ActionEvent.ANY , btnActionFilter );
// シーンにキー入力処理を追加する
EventHandler<KeyEvent> sceneKeyFilter = ( event ) -> System.out.println( "key input(" + event.getText() + ")" );
scene.addEventFilter( KeyEvent.KEY_PRESSED , sceneKeyFilter );
/* 以下、イベントの処理順を確認するためのフィルターとハンドラ */
// ラベルにマウスクリック処理用のイベント・フィルターを設定
EventHandler<MouseEvent> btnClickFilter = ( event ) -> System.out.println( "( Filter ) mouse click! " );
label.addEventFilter( MouseEvent.MOUSE_PRESSED , btnClickFilter );
// ラベルにマウスクリック処理用のイベント・ハンドラを設定
EventHandler<MouseEvent> btnHandler = ( event ) -> System.out.println( "( Handler ) mouse click! " );
label.addEventHandler( MouseEvent.MOUSE_PRESSED , btnHandler );
// シーンにマウスクリック処理用のイベント・フィルターを設定
EventHandler<MouseEvent> sceneClickFilter= ( event ) -> System.out.println( "( Filter ) scene mouse click!" );
scene.addEventFilter( MouseEvent.MOUSE_PRESSED , sceneClickFilter );
// シーンにマウスクリック処理用のイベント・ハンドラを設定
EventHandler<MouseEvent> sceneHandler = ( event ) -> System.out.println( "( Handler ) scene button click!" );
scene.addEventHandler( MouseEvent.MOUSE_PRESSED , sceneHandler );
// ステージにマウスクリック処理用のイベント・フィルターを設定
EventHandler<MouseEvent> stageClickFilter= ( event ) -> System.out.println( "( Filter ) stage mouse click!" );
primaryStage.addEventFilter( MouseEvent.MOUSE_PRESSED , stageClickFilter );
// ステージにマウスクリック処理用のイベント・ハンドラを設定
EventHandler<MouseEvent> stageHandler = ( event ) -> System.out.println( "( Handler ) stage button click!" );
primaryStage.addEventHandler( MouseEvent.MOUSE_PRESSED , stageHandler );
// ウィンドウ表示
primaryStage.setScene( scene );
primaryStage.show();
}
}
◇実行結果
(ラベルをクリックした場合)
( Filter ) stage mouse click!
( Filter ) scene mouse click!
( Filter ) mouse click!
( Handler ) mouse click!
( Handler ) scene button click!
( Handler ) stage button click!
(ボタンをクリックした場合)
( Filter ) stage mouse click!
( Filter ) scene mouse click!
button push!
(『e』キーを入力した場合)
key input(e)
◇ソース解説
- (24行目~31行目) シーンとシーングラフを準備。
- (34行目~36行目) ボタンに対して、ボタン押下時のイベント・ハンドラを登録。イベント・ハンドラをラムダ式記法で記述していることに注意。ボタンをクリックした場合の実行結果を見ると、イベント・ハンドラ関数内でEvent.consume関数を呼び出したためにStageとSceneのイベント・ハンドラが呼び出されていないことが確認できる。
- (38行目~40行目) シーンに対して、キー入力時のイベント・フィルタを登録
- (43行目~67行目) イベント・ハンドラ及びフィルタの処理順を確認するため、ステージ、シーン、ラベルに対してマウス押下時のイベント・ハンドラ/フィルタを登録。ラベルをクリックした場合の実行結果を見ると、処理順の違いが確認できる。
■ 参照
- Java Platform, Standard Edition (Java SE) 8 - チュートリアル(日本語) - JavaFX: イベントの処理
- JavaDoc - クラスEvent
改訂履歴・2015年10月26日 一部改訂。『イベントの種類』を追記