4 イベント・ハンドラの使用
このトピックでは、JavaFXアプリケーションにおけるイベント・ハンドラについて説明します。キーボード・アクション、マウス・アクション、スクロール・アクションなど、ユーザーがアプリケーションと対話するときに生成されるイベントをイベント・ハンドラによって処理する方法を取り上げます。
イベント・ハンドラを使用すると、イベント・バブリング・フェーズでイベントを処理できます。ノードには、イベントを処理するためのハンドラを1つ以上登録できます。1つのハンドラを複数のノードおよび複数のイベント・タイプに使用できます。子ノードのイベント・ハンドラがイベントを消費しない場合、親ノードのイベント・ハンドラを通して、子ノードでのイベント処理後に親ノードがそのイベントに作用できるようにしたり、複数の子ノードのイベント処理を共通化できます。
イベント・ハンドラの登録および削除
イベント・バブリング・フェーズでイベントを処理するには、ノードにイベント・ハンドラを登録する必要があります。イベント・ハンドラは、EventHandler
インタフェースの実装です。ハンドラが登録されているノードにそのハンドラに関連付けられているイベントが渡された場合に、このインタフェースのhandle()
メソッドを通してコードが実行されます。
ハンドラを登録するには、addEventHandler()
メソッドを使用します。このメソッドには、引数としてイベント・タイプとハンドラを渡します。例4-1では、1つ目のハンドラを単一のノードに追加し、そのハンドラにより特定のイベント・タイプを処理するように指定しています。入力イベントを処理するための2つ目のハンドラを2つの異なるノードに定義および登録しています。また、同じハンドラを2つの異なるイベント・タイプ用に登録しています。
例4-1 ハンドラの登録
// Register an event handler for a single node and a specific event type node.addEventHandler(DragEvent.DRAG_ENTERED, new EventHandler<DragEvent>() { public void handle(DragEvent) { ... }; }); // Define an event handler EventHandler handler = new EventHandler(<InputEvent>() { public void handle(InputEvent event) { System.out.println("Handling event " + event.getEventType()); event.consume(); } // Register the same handler for two different nodes myNode1.addEventHandler(DragEvent.DRAG_EXITED, handler); myNode2.addEventHandler(DragEvent.DRAG_EXITED, handler); // Register the handler for another event type myNode1.addEventHandler(MouseEvent.MOUSE_DRAGGED, handler);
1つのイベント・タイプ用として定義したイベント・ハンドラは、そのイベントのサブタイプにも使用できます。イベント・タイプの階層の詳細は、「イベント・タイプ」を参照してください。
イベント・ハンドラによるイベントの処理が特定のノードまたはイベント・タイプに対して不要になった場合は、removeEventHandler()
メソッドを使用してハンドラを削除します。このメソッドには、引数としてイベント・タイプとハンドラを渡します。例4-2では、例4-1で定義したハンドラをmyNode1
のDragEvent.DRAG_EXITED
イベントに対して削除しています。MouseEvent.MOUSE_DRAGGED
イベントに対しては、myNode2
およびmyNode1
で引き続きハンドラが実行されます。
ヒント:
コンビニエンス・メソッドによって登録されたイベント・ハンドラを削除するには、node1.setOnMouseDragged(null)
のように、コンビニエンス・メソッドにnullを渡します。
イベント・ハンドラの使用
イベント・ハンドラは、一般にイベント・ディスパッチ・チェーンのリーフ・ノードまたはブランチ・ノードで使用し、イベント処理のイベント・バブリング・フェーズでコールします。すべての子ノードに対するデフォルト・レスポンスを定義する場合などに、ブランチ・ノードでハンドラを使用します。
ハンドラの使用方法を示す例を参照するには、KeyboardExample.zip
ファイルをダウンロードします。NetBeansプロジェクトを抽出し、NetBeans IDEで開きます。次の項では、この例で使用されているハンドラについて説明します。
キーボードの例
キーボードの例では、次のハンドラの使用方法を例示しています。
-
1つのハンドラを2つの異なるイベント・タイプ用として登録します。
-
親ノードで子ノードのイベント処理を共通化します。
図4-1に、キーボードの例を開始したときに表示される画面を示します。ユーザー・インタフェースは、対応するキーボード・キーを表す4つの文字で構成されます(各文字はそれぞれ四角形で囲まれています)。画面上の1つ目のキーが強調表示されているのは、そのキーにフォーカスがあることを示しています。画面上の別のキーにフォーカスを移動するには、キーボード上の左矢印キーと右矢印キーを使用します。
[Enter]キーを押すと、画面上のフォーカスのあるキーが赤に変わります。[Enter]キーを放すと、画面上のキーが元の色に戻ります。画面上のいずれかのキーに対応する文字キーを押すと、画面上の対応するキーが赤に変わり、キーを放すと、元の色に戻ります。画面上のいずれのキーにも対応しないキーを押した場合は、何も起こりません。図4-2は、[A]キーにフォーカスがあり、キーボード上の[D]キーを押した場合の画面です。
キーボードの例のハンドラ
キーボードの例では、画面上の各キーは内部的にキー・ノードで表されます。すべてのキー・ノードが単一のキーボード・ノードに含まれています。各キー・ノードには、そのキーにフォーカスが移動したときにキー・イベントが渡されるハンドラが登録されています。[Enter]キーのキー押下イベントおよびキー解放イベントに対するハンドラのレスポンスとして、画面上のキーの色が変更されます。そのイベントは消費され、親ノードであるキーボード・ノードには渡されません。
例4-3に、キー・ノードのハンドラを定義するinstallEventHandler()
メソッドを示します。
例4-3 キー・ノードのハンドラ
private void installEventHandler(final Node keyNode) { // handler for enter key press / release events, other keys are // handled by the parent (keyboard) node handler final EventHandler<KeyEvent> keyEventHandler = new EventHandler<KeyEvent>() { public void handle(final KeyEvent keyEvent) { if (keyEvent.getCode() == KeyCode.ENTER) { setPressed(keyEvent.getEventType() == KeyEvent.KEY_PRESSED); keyEvent.consume(); } } }; keyNode.setOnKeyPressed(keyEventHandler); keyNode.setOnKeyReleased(keyEventHandler); }
キーボード・ノードには、キー・ノードのハンドラで消費されないキー・イベントを処理する2つのハンドラがあります。1つ目のハンドラでは、対応するキーが押されたときにキー・ノードの色を変更します。2つ目のハンドラでは、左矢印キーおよび右矢印キーが押されたときにフォーカスを移動します。
例4-4に、キーボード・ノードのハンドラを定義するinstallEventHandler()
メソッドを示します。
例4-4 キーボード・ノードのハンドラ
private void installEventHandler(final Parent keyboardNode) { // handler for key pressed / released events not handled by // key nodes final EventHandler<KeyEvent> keyEventHandler = new EventHandler<KeyEvent>() { public void handle(final KeyEvent keyEvent) { final Key key = lookupKey(keyEvent.getCode()); if (key != null) { key.setPressed(keyEvent.getEventType() == KeyEvent.KEY_PRESSED); keyEvent.consume(); } } }; keyboardNode.setOnKeyPressed(keyEventHandler); keyboardNode.setOnKeyReleased(keyEventHandler); keyboardNode.addEventHandler(KeyEvent.KEY_PRESSED, new EventHandler<KeyEvent>() { public void handle( final KeyEvent keyEvent) { handleFocusTraversal( keyboardNode, keyEvent); } }); }
キー押下イベントの2つのハンドラはピア・ハンドラとみなされます。したがって、一方のハンドラでイベントが消費されても、もう一方のハンドラも呼び出されます。