JavaFX KeyEvent传播顺序 [英] JavaFX KeyEvent propagation order

查看:131
本文介绍了JavaFX KeyEvent传播顺序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在我的场景中听一些KeyEvent,比如说 KeyCode.ESCAPE (按下时关闭场景)。

I want to listen to some KeyEvent in my scene, say KeyCode.ESCAPE(close the scene when pressed).

scene.addEventHandler(KeyEvent.ANY, event -> {
            if (event.isConsumed())
                return;
            switch (event.getCode()) {
            case ESCAPE:
                stage.hide();
                event.consume();
                break;
            default:
                break;
            }
        });

现在,场景中的节点可能已经收听 ESCAPE 也是。

Now, the nodes inside the scene could have listened to ESCAPE too.

// ....
someOtherNode.addEventHandler(KeyEvent.ANY, e -> {
        if (e.getCode() == KeyCode.ESCAPE) {
            // do stuff
            e.consume();
        }
});
// ....

如何确保 KeyEvent 将从节点而不是场景消耗?

How do I make sure that the KeyEvent will be consumed from the node and not the scene?

根据Oracle的图表,解决方法是添加一个虚拟对象节点在节点层次结尾处,侦听 KeyCode s

Based on the diagram from Oracle, A workaround would be adding a dummy Node at the end of the Node hierarchy that listens to KeyCodes

但是有更好的解决方案,比如反转传播路径吗?

编辑:

用例:

阻止其他节点的类似弹出式节点需要侦听ESC键或 focusProperty()以便它可以自行关闭。

A popup-like node that blocks other nodes would need to listens to the ESC key or focusProperty() so that it can close itself.

推荐答案

有两种方法可以影响事件:

There's two ways you can affect events:


  1. 使用 Node.addEventFilter(...) 注册过滤器的方法。过滤器将在事件的捕获阶段执行(随着窗口越来越具体,确定哪些节点应该获得事件)。

  1. Use the Node.addEventFilter(...) method to register a filter. A filter will execute on the capturing phase of the event (as the window is getting more specific, determining which Nodes should get the event).

使用< a href =http://docs.oracle.com/javafx/2/api/javafx/scene/Node.html#addEventHandler%28javafx.event.EventType,%20javafx.event.EventHandler%29> Node.addEventHandler(...) 注册处理程序的方法。处理程序将从捕获阶段中找到的最特定节点开始执行,向下直到消耗为止。

Use the Node.addEventHandler(...) method to register a handler. The handler will execute starting at the most specific node found in the capturing phase, heading down until it is consumed.

因此在捕获阶段,会创建一个堆栈。从窗口(最顶层的父节点)开始,此事件可能执行的每个节点都将添加到堆栈中(以最底层的子节点结束)。过滤器可以中断此过程,或者只是在此过程中执行事件。

So in the capturing phase, a stack is created. Starting with the window (topmost parent), each node that this event could potentially execute on is added to the stack (ending with the bottom most child). A filter can interrupt this process, or just execute an event during this process.

在冒泡阶段,事件处理程序将从堆栈顶部开始触发(创建在捕获阶段)直到堆栈为空或事件被消耗。

In the bubbling phase, the event handlers will start firing from the top of the stack (created in the capturing phase) until the stack is empty or the event is consumed.

在你的情况下,你真的不应该担心任何事情。如果任何节点关心处理ESC事件,他们将在冒泡阶段这样做(并且他们应该使用该事件以防止进一步处理)。您可以在 ComboBox 中看到此行为。如果他们不在乎,它将冒泡到 Scene ,并且该处理程序将执行。只需确保您创建的处理ESC按下的任何自定义代码也会消耗该事件。

In your case, you really shouldn't have anything to worry about. If any node cares about processing the "ESC" event, they will do so in the bubbling phase (and they should consume the event to prevent further processing). You can see this behavior in the ComboBox. If they don't care, it will bubble up to your Scene and that handler will execute. Just make sure any custom code you create that processes an "ESC" press also consumes that event.

有关详细信息,请参阅此处的说明和教程: http://docs.oracle.com/javafx/2/events/jfxpub-events.htm

For more information, there is a explanation and tutorial here: http://docs.oracle.com/javafx/2/events/jfxpub-events.htm

以下是一些演示Escape功能的示例代码。在聚焦 ComboBox 时按ESC不会导致应用程序关闭,而它将关闭其他控件。

And here is some sample code demonstrating the Escape functionality. Pressing ESC while focused on the ComboBox will not cause the application to close, while it will close with the other controls.

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
import javafx.util.converter.DefaultStringConverter;


public class FXEventFiltering extends Application {

    public static void main(String[] args) { launch(args); }

    @Override
    public void start(final Stage stage) throws Exception {
        //All the controls are added here
        VBox box = new VBox();
        ComboBox<String> dropdown = new ComboBox<>();
        TextField field = new TextField();
        CheckBox check = new CheckBox("Check");
        RadioButton radio = new RadioButton("Radio!");
        TextArea area = new TextArea();
        TableView<String> table = new TableView<String>(FXCollections.observableArrayList(new String[]{"one","two"}));
        TableColumn<String, String> tc = new TableColumn<String, String>("Column1");
        tc.setEditable(true);
        tc.setCellFactory(TextFieldTableCell.<String,String>forTableColumn(new DefaultStringConverter()));
        tc.setCellValueFactory(new Callback<CellDataFeatures<String,String>, ObservableValue<String>>(){
            @Override
            public ObservableValue<String> call(CellDataFeatures<String, String> arg0) {
                return new SimpleStringProperty(arg0.getValue());
            }});
        table.getColumns().add(tc);

        box.getChildren().addAll(dropdown, field, check, radio, area, table);

        //Setting up your scene
        Scene scene = new Scene(box);
        stage.setScene(scene);
        scene.addEventHandler(KeyEvent.ANY, new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                System.out.println("KEYS!" + event.getEventType().getName());
                switch (event.getCode()) {
                case ESCAPE:
                    System.out.println("Escape!");
                    stage.hide();
                    event.consume();
                    break;
                default:
                    break;
                }
            }
        });

        box.requestFocus(); // Removing default focus

        stage.show();
    }

}

这篇关于JavaFX KeyEvent传播顺序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆