如何通过按键盘上的特定键 (b) 来更改场景?

How can I change the scene By pressing a specific key(b) on the the keyboard?

在我的应用程序中,有两个场景:mainScenebossScene,其中mainScene是在启动应用程序时使用的。

我的代码:

    public void start(Stage stage) throws Exception {

    stage.setTitle("BossKey Example");

    // Scene and layout for the main view
    VBox root = new VBox();
    Scene mainScene = new Scene(root, 500, 300);

    // Scene for the BOSS view
    Scene bossScene = new Scene(new Label("Nothing suspicious here"), 500, 300);

    List<TextField> fields = new ArrayList<TextField>();
    for (int i = 0; i < 10; i++) {
        fields.add(new TextField());

    }

    fields.setOnKeyPressed(new EventHandler<KeyEvent>() {
        @Override
        public void handle(KeyEvent keyEvent) {
            switch (keyEvent.getCharacter()){
                case "b": stage.setScene(bossScene); break;
            }
        }
    });

/////// 添加了 addEventFilter,但仍然无法正常工作

   mainScene.addEventFilter(KeyEvent.KEY_PRESSED, new 
             EventHandler<KeyEvent() {

        @Override
        public void handle(KeyEvent keyEvent) {
            switch (keyEvent.getCharacter()){
                case "b": stage.setScene(bossScene); break;
            }
            keyEvent.consume();

        }

            });


    // Create components for main view
    root.getChildren().addAll(fields);
    root.getChildren().add(new Button("Hello!"));

    stage.setScene(mainScene);
    stage.show();

}

}

键组合过滤器

您应该在事件过滤器中使用组合键,例如 CTRL+B 或 SHORTCUT+B。

有关如何应用组合键的详细信息,请参阅:

  • javafx keyboard event shortcut key

为什么组合键优于过滤字符“b”:

  • 如果您过滤“b”字符,则如果大写锁定已关闭,该功能将无法使用。
  • 如果您筛选“b”字符,您将无法在文本字段中键入“b”。

您可能认为您可以编写 scene.setOnKeyPressed(...),但是,在许多情况下,这不会像预期的那样工作。需要过滤器而不是按键事件处理程序,因为如果您使用处理程序,按键事件可能会被文本字段等焦点字段使用,因此处理程序实现可能不会在所有需要的情况下都激活。

过滤组合键避免了尝试处理字符按键时出现的问题。组合键依赖于表示按下的物理键的键码,而不依赖于其他键的状态,例如大写锁定,除非您为此明确添加额外的逻辑。

如果您不了解事件过滤器和事件处理程序之间的区别以及事件派发的捕获和冒泡阶段,请学习:

KeyCombination 过滤器实现

final EventHandler<KeyEvent> bossEventFilter = new EventHandler<>() {
    final KeyCombination bossKeyCombo = new KeyCodeCombination(
            KeyCode.B,
            KeyCombination.CONTROL_DOWN
    );

    public void handle(KeyEvent e) {
        if (bossKeyCombo.match(e)) {
            if (stage.getScene() == mainScene) {
                stage.setScene(bossScene);
            } else if (stage.getScene() == bossScene) {
                stage.setScene(mainScene);
            }

            e.consume();
        }
    }
};

mainScene.addEventFilter(KeyEvent.KEY_PRESSED, bossEventFilter);
bossScene.addEventFilter(KeyEvent.KEY_PRESSED, bossEventFilter);

加速器备选方案

可以使用加速器代替事件过滤器。关于应用加速器的信息也在链接问题的答案中,我不会在这里进一步详细说明这个替代方案。

示例解决方案

独立可执行示例代码:

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.*;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import java.io.IOException;

public class SceneSwap extends Application {
    @Override
    public void start(Stage stage) throws IOException {
        final Scene mainScene = new Scene(
                createLayout(
                        "Press CTRL+B to enter boss mode",
                        Color.PALEGREEN
                )
        );
        final Scene bossScene = new Scene(
                createLayout(
                        "Press CTRL+B to exit boss mode",
                        Color.PALEGOLDENROD
                )
        );

        final EventHandler<KeyEvent> bossEventFilter = new EventHandler<>() {
            final KeyCombination bossKeyCombo = new KeyCodeCombination(
                    KeyCode.B,
                    KeyCombination.CONTROL_DOWN
            );

            public void handle(KeyEvent e) {
                if (bossKeyCombo.match(e)) {
                    if (stage.getScene() == mainScene) {
                        stage.setScene(bossScene);
                    } else if (stage.getScene() == bossScene) {
                        stage.setScene(mainScene);
                    }

                    e.consume();
                }
            }
        };

        mainScene.addEventFilter(KeyEvent.KEY_PRESSED, bossEventFilter);
        bossScene.addEventFilter(KeyEvent.KEY_PRESSED, bossEventFilter);

        stage.setScene(mainScene);
        stage.show();
    }

    private VBox createLayout(String text, Color color) {
        VBox mainLayout = new VBox(10,
                new Label(text),
                new TextField()
        );
        mainLayout.setPadding(new Insets(10));
        mainLayout.setStyle("-fx-background: " + toCssColor(color));

        return mainLayout;
    }

    private String toCssColor(Color color) {
        int r = (int) Math.round(color.getRed() * 255.0);
        int g = (int) Math.round(color.getGreen() * 255.0);
        int b = (int) Math.round(color.getBlue() * 255.0);
        int o = (int) Math.round(color.getOpacity() * 255.0);

        return String.format("#%02x%02x%02x%02x" , r, g, b, o);
    }

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