4 使用事件处理程序
本主题描述了JavaFX应用程序中的事件处理程序。了解如何使用事件处理程序来处理键盘操作、鼠标操作、滚动操作和其他用户与应用程序的交互所生成的事件。
事件处理程序使您能够在事件冒泡阶段处理事件。一个节点可以有一个或多个处理程序来处理事件。一个处理程序可以用于多个节点和多个事件类型。如果子节点的事件处理程序没有消耗事件,父节点的事件处理程序可以在子节点处理事件后对事件进行处理,并为多个子节点提供共同的事件处理。
注册和移除事件处理器
要在事件冒泡阶段处理事件,节点必须注册一个事件处理器。事件处理器是EventHandler
接口的实现。该接口的handle()
方法提供了当与处理器关联的事件被注册处理器的节点接收到时执行的代码。
要注册处理器,使用addEventHandler()
方法。该方法接受事件类型和处理器作为参数。在示例4-1中,第一个处理器被添加到一个单独的节点,并处理特定的事件类型。定义了第二个处理输入事件的处理器,并由两个不同的节点注册。同一个处理器也被注册到两种不同类型的事件。
示例4-1 注册处理器
// 为单个节点和特定的事件类型注册事件处理器 node.addEventHandler(DragEvent.DRAG_ENTERED, new EventHandler<DragEvent>() { public void handle(DragEvent) { ... }; }); // 定义一个事件处理器 EventHandler handler = new EventHandler(<InputEvent>() { public void handle(InputEvent event) { System.out.println("处理事件 " + event.getEventType()); event.consume(); } // 为两个不同的节点注册相同的处理器 myNode1.addEventHandler(DragEvent.DRAG_EXITED, handler); myNode2.addEventHandler(DragEvent.DRAG_EXITED, handler); // 为另一种事件类型注册处理器 myNode1.addEventHandler(MouseEvent.MOUSE_DRAGGED, handler);
请注意,为一种事件类型定义的事件处理器也可以用于该事件的任何子类型。有关事件类型层次结构的信息,请参见事件类型。
当您不再希望事件处理器处理节点或事件类型的事件时,请使用removeEventHandler()
方法移除处理器。该方法接受事件类型和处理器作为参数。在示例4-2中,从myNode1
的DragEvent.DRAG_EXITED
事件中移除了在示例4-1中定义的处理器。该处理器仍然由myNode2
和myNode1
执行MouseEvent.MOUSE_DRAGGED
事件。
提示:
要移除由便利方法注册的事件处理器,请将null传递给便利方法,例如node1.setOnMouseDragged(null)
。
使用事件处理器
事件处理器通常用于事件分发链的叶节点或分支节点,并在事件处理的冒泡阶段被调用。在分支节点上使用处理器可以执行诸如为所有子节点定义默认响应等操作。
要查看处理器的使用示例,请下载KeyboardExample.zip
文件。解压缩NetBeans项目并在NetBeans IDE中打开。以下部分描述了此示例使用的处理器。
键盘示例
键盘示例演示了以下处理器的用法:
-
为两种不同的事件类型注册单个处理器
-
为父节点中的子节点提供公共事件处理
图4-1是启动键盘示例时显示的屏幕。用户界面由四个字母组成,每个字母都在自己的方框中,代表相应的键盘按键。屏幕上的第一个键被突出显示,表示它具有焦点。使用键盘上的左右箭头键将焦点移动到屏幕上的不同键。
按下Enter键时,屏幕上具有焦点的键将变为红色。释放Enter键时,屏幕上的键将恢复到之前的颜色。当按下与屏幕上的键匹配的字母键时,屏幕上的匹配键将变为红色,并在释放键时恢复到之前的颜色。当按下与屏幕上任何键都不匹配的键时,不会发生任何操作。 图4-2显示了当A键具有焦点且键盘上的D键被按下时的屏幕。
键盘示例的事件处理程序
在键盘示例中,屏幕上显示的每个键都由一个键节点表示。所有键节点都包含在一个单独的键盘节点中。每个键节点都有一个处理程序,当键获得焦点时,该处理程序接收键事件。处理程序对于Enter键的按下和释放事件做出响应,改变屏幕上键的颜色。然后,事件被消耗,以使作为父节点的键盘节点不接收该事件。
示例4-3显示了installEventHandler()
方法,该方法定义了键节点的处理程序。
示例4-3 键节点的处理程序
private void installEventHandler(final Node keyNode) { // 处理Enter键按下/释放事件,其他键由父节点(键盘)处理程序处理 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); }
键盘节点有两个处理程序,用于处理键节点处理程序未消耗的键事件。第一个处理程序改变与按下的键匹配的键节点的颜色。第二个处理程序响应左右箭头键并移动焦点。
示例4-4显示了installEventHandler()
方法,该方法定义了键盘节点的处理程序。
示例4-4 键盘节点的处理程序
private void installEventHandler(final Parent keyboardNode) { // 处理键节点未处理的按下/释放事件 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); } }); }
对于按下事件的两个处理程序被视为对等处理程序。因此,即使每个处理程序都消耗了事件,另一个处理程序仍然会被调用。