这是 javafx 中事件过滤器和 requstfocus 的正确行为吗?

Is this the proper behaviour for event filter and requstfocus in javafx?

我有一个非常简单的任务要完成。我只想按下按钮上的任何字母,匹配键码,然后将焦点移动到文本字段。我写 一个简单的测试代码如图所示。我转移焦点没问题。然而, 我不希望我按下的字母出现在文本字段中。看似简单的编程解决方案,其实并没有那么简单。

我不明白为什么事件使用方法不阻止事件在事件链中向下传播并在文本字段中显示键入的字母。

似乎在调用 requestFocus 之后,文本字段会拾取从按钮键入的字母。这发生在 Mac。任何帮助将不胜感激。

package testkeynavigation;


public class TestKeyNavigation extends Application {

@Override
public void start(Stage primaryStage) {
    Button btn = new Button();
    btn.setText("Say 'Hello World'");
    btn.setOnAction(new EventHandler<ActionEvent>() {

        @Override
        public void handle(ActionEvent event) {
            System.out.println("Hello World!");
        }
    });

    StackPane root = new StackPane();

    TextField txt1 = new TextField();
    TextField txt2 = new TextField();
    VBox vbox = new VBox();
    vbox.getChildren().add(btn);
    vbox.getChildren().add(txt1);
    vbox.getChildren().add(txt2);
    root.getChildren().add(vbox);

    Scene scene = new Scene(root, 300, 250);


    btn.setOnKeyPressed((KeyEvent e) ->{
        if (e.getCode() == KeyCode.A) {
            e.consume();
            System.out.println("e.isConsumed: "+e.isConsumed());
            txt2.requestFocus();
        }

    });

    primaryStage.setTitle("Hello World!");
    primaryStage.setScene(scene);
    primaryStage.show();

    btn.requestFocus();
}

/**
 * @param args the command line arguments
 */
public static void main(String[] args) {
    launch(args);
}

}

按键事件有KEY_PRESSEDKEY_TYPEDKEY_RELEASED三种。在一次击键中,每种类型的事件都会按此顺序触发到事件发生时具有键盘焦点的 UI 节点。

A TextField 有一个 KEY_TYPED 事件的内部侦听器;因此,如果在文本字段具有焦点时发生 KEY_TYPED 事件,则会在文本字段中输入一个字符(或发生其他操作,例如删除字符或移动插入符号,具体取决于键)。

在您的代码中,您监听其中的第一个 - KEY_PRESSED - 出现在按钮上。如果按键的代码为 A,您将使用 that 事件(KEY_PRESSED 事件),然后将键盘焦点转移到文本字段。稍后,用户松开按键,由于文本字段现在获得焦点,因此在文本字段上触发 KEY_TYPED 事件。请注意,这是一个新事件,因此不会消耗它,因此文本字段的反应就像输入了一个字符一样。最后,在文本字段上触发 KEY_RELEASED 事件。

如果添加调试代码,您可以看到实际效果:

txt2.addEventFilter(KeyEvent.ANY, e -> {
    System.out.printf("Key event on text field: type=%s, code=%s, character=%s%n", 
            e.getEventType(), e.getCode(), e.getCharacter());
});

要解决这个问题,只需监听事件系列中的最后一个事件:按键释放事件。请注意,您不需要使用该事件。

public void start(Stage primaryStage) {
    Button btn = new Button();
    btn.setText("Say 'Hello World'");
    btn.setOnAction(new EventHandler<ActionEvent>() {

        @Override
        public void handle(ActionEvent event) {
            System.out.println("Hello World!");
        }
    });

    StackPane root = new StackPane();

    TextField txt1 = new TextField();
    TextField txt2 = new TextField();
    VBox vbox = new VBox();
    vbox.getChildren().add(btn);
    vbox.getChildren().add(txt1);
    vbox.getChildren().add(txt2);
    root.getChildren().add(vbox);

    Scene scene = new Scene(root, 300, 250);


    btn.setOnKeyReleased((KeyEvent e) ->{
        if (e.getCode() == KeyCode.A) {
            txt2.requestFocus();
        }

    });

    txt2.addEventFilter(KeyEvent.ANY, e -> {
        System.out.printf("Key event on text field: type=%s, code=%s, character=%s%n", 
                e.getEventType(), e.getCode(), e.getCharacter());
    });

    primaryStage.setTitle("Hello World!");
    primaryStage.setScene(scene);
    primaryStage.show();

    btn.requestFocus();
}