【问题标题】:JavaFX KeyEvent propagation orderJavaFX KeyEvent 传播顺序
【发布时间】:2023-03-29 18:42:01
【问题描述】:

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

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

现在,场景中的节点也可以监听ESCAPE

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

如何确保KeyEvent 将从节点而不是场景中使用?

根据 Oracle 的图表,解决方法是在侦听 KeyCodes 的节点层次结构的末尾添加一个虚拟 Node

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

编辑:

用例:

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

【问题讨论】:

  • 我可能很密集,但这不是过滤器和处理程序的用途吗?过滤器在捕获阶段停止事件(如果已消耗)。事后事件处理程序可以在冒泡阶段触发。一个是另一个的倒数。

标签: java javafx javafx-2 javafx-8


【解决方案1】:

您可以通过两种方式影响事件:

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

  2. 使用Node.addEventHandler(...) 方法注册处理程序。处理程序将从捕获阶段找到的最具体的节点开始执行,一直向下直到它被消耗。

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

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

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

更多信息,这里有解释和教程:http://docs.oracle.com/javafx/2/events/jfxpub-events.htm

这里是一些演示 Escape 功能的示例代码。在关注 ComboBox 时按 ESC 不会导致应用程序关闭,但会与其他控件一起关闭。

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();
    }

}

【讨论】:

    【解决方案2】:

    也许你可以在捕捉到场景中的事件后遍历所有节点以找出哪个节点具有实际焦点?那你可以调用node方法关闭吗?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-02
      • 2011-04-05
      • 1970-01-01
      相关资源
      最近更新 更多