JavaFX KeyEvent传播顺序

时间:2014-02-09 08:40:50

标签: java javafx javafx-2 javafx-8

我想在我的场景中听一些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的图表,一种解决方法是在Node层次结尾处添加一个虚拟Node,以监听KeyCode s

enter image description here

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

修改

用例:

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

2 个答案:

答案 0 :(得分:6)

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

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

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

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

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

    在你的情况下,你真的不应该担心。如果任何节点关心处理“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();
        }
    
    }
    

答案 1 :(得分:0)

也许你可以在场景中捕获事件之后遍历所有节点,找出哪个节点有实际焦点?然后你可以调用node方法来关闭?