【问题标题】:How to set an action to occur on pressing of any keyboard key in JavaFX?如何设置在按下 JavaFX 中的任何键盘键时发生的操作?
【发布时间】:2021-09-04 22:03:45
【问题描述】:

所以我想为大学的视频游戏项目制作标题菜单。我想显示一条消息,按任意键继续...然后当用户按下任意键(包括鼠标)时,将运行一个方法将舞台设置为下一个菜单。

我在下面发布了所有相关代码,但简短的版本是:

  • BackgroundImangeDisplayPane 扩展显示窗格并添加背景图像。
  • TitleCard extends BackgroundImangeDisplayPane 为 VBox 添加一个 VBox 和 2 个标签
  • 我用public void start(Stage primaryStage) throws Exception为主,这里设置了setOnActionxxx方法

我尝试在 root 和 Vbox 上使用 set on action 方法,但它们都不起作用...当我单击时没有任何反应...但是当我调整窗口大小时 root.setOnActionXXX“激活”。

如果我在 TitleCard 类上编写 setOnAction 方法,它有点工作,但我无法切换阶段。

我将在下面发布代码以及对场景结构的解释,它并不复杂:


// this will be the borderpane for every scene it recives a backgund 
//images that will be present in every menu
public BackgroundImangeDisplayPane() {
        try {
            stream = new FileInputStream(imagePath.toString());
            Image image = new Image(stream);
            ImageView imageView = new ImageView();
            imageView.setImage(image);
            imageView.setFitWidth(1920);
            imageView.setPreserveRatio(true);
            this.getChildren().add(imageView);

            BackgroundSize backgoundSize = new BackgroundSize(AUTO, AUTO, true, true, true, true);
            BackgroundImage backgroundImage = new BackgroundImage(image, NO_REPEAT, NO_REPEAT, CENTER, backgoundSize);
            Background background = new Background(backgroundImage);
            this.setBackground(background);

        } catch (Exception e) {

        }
    }


//This extends `BackgroundImangeDisplayPane` and places on top of it a A Vbox with two lables: the title and "press any key to continue..."
// it then adds styles to the labels
public class TitleCard extends BackgroundImangeDisplayPane {
    Label title = new Label("Boats & Docks"); // lable 1
    Label subtitle = new Label("Press any key to continue ..."); label2
    
    public TitleCard(){
        super();
        VBox vbox = new VBox();
        vbox.getChildren().add(title);
        vbox.getChildren().add(subtitle);
        
        this.setCenter(vbox);
        this.setAlignment(vbox, Pos.CENTER);
        vbox.setAlignment(Pos.CENTER);
        title.setFont(new Font(170)); // set to Label
        title.setTextFill(Color.SNOW);
        title.setEffect(new DropShadow());
        subtitle.setFont( new Font (30));
          
    }
}

... 
//Works as the "main" in javaFX
private Stage primaryStage;

    @Override
    public void start(Stage primaryStage) throws Exception {

        TitleCard root = new TitleCard();
        /*BasicMenu menu = new BasicMenu(5);
        menu.ButtonSetOnAction(0, e -> changeScene() );
        BackgroundImangeWithCustomMenu background = new 
        BackgroundImangeWithCustomMenu(menu,50,50);
        root.setCenter(background);*/
        Button b = new Button();
        b.setOnAction(e -> changeSceneToLoginMenu());
        System.out.println(root.getChildren().get(1).getClass());
        root.getChildren().get(1).setFocusTraversable(true);
        root.getChildren().get(1).setOnMouseClicked(e -> changeSceneToLoginMenu());
        root.getChildren().get(1).setOnKeyPressed(e -> changeSceneToLoginMenu());
        root.getChildren().get(0).setOnMouseClicked(e -> changeSceneToLoginMenu());
        root.getChildren().get(0).setOnKeyPressed(e -> changeSceneToLoginMenu());
/*
        root.setOnMouseClicked(e -> changeSceneToLoginMenu());
        root.setOnKeyReleased(e -> changeSceneToLoginMenu());
        root.setOnKeyPressed(e -> changeSceneToLoginMenu());
*/
        Scene scene = new Scene(root, 1280, 720);
        primaryStage.setScene(scene);
        primaryStage.show();

        this.primaryStage = primaryStage;

    }

【问题讨论】:

  • 老实说,JavaFX 或 Java Swing 不适合用于视频游戏之类的东西。查看用于 Java 游戏开发的 LWJGL 或 LibGDX 之类的东西。 (至于你的实际问题,我将不得不看看,它充其量是棘手的。)
  • 以下是我在 SO 上找到的一些潜在答案:stackoverflow.com/questions/29962395/…
  • 不可能是大学项目。
  • 这有什么关系?这些不是商业产品,它们是免费的开源框架。
  • 在场景中注册一个关键监听器。

标签: java javafx


【解决方案1】:

按照 James 在 cmets 中的建议,当出现 key is pressed on the scene 时,导航到下一个场景(或者替换当前场景中的根,并移除按键处理程序)。

scene.setOnKeyPressed(e -> navigateToNextScene());

【讨论】:

    【解决方案2】:

    我设法找到了一个非常简单的工作解决方案,但我并不完全不明白为什么它会起作用。我注意到如果我将处理程序设置在同一个类中,则节点被实例化,处理程序会正常工作但是如果我尝试通过root.getChildren().get(1) 使用主函数的方法获取节点然后强制转换将它添加到处理程序不起作用的 VBox 元素。

    作为解决方案我将 VBox 设为字段,并为 TitleCard 类中的 VBox 事件处理程序编写了一个 setter 方法。这解决了问题。

    我用 cmets 将添加的代码标记为解决方案代码

    
    public class TitleCard extends BackgroundImangeDisplayPane {
        
        Label title = new Label("Boats & Docks"); // lable 1
        Label subtitle = new Label("Press any key to continue ..."); label2
        VBox vbox = new VBox; // solution code
        
    public TitleCard(){
            super();
            VBox vbox = new VBox();
            vbox.getChildren().add(title);
            vbox.getChildren().add(subtitle);
            
            this.setCenter(vbox);
            this.setAlignment(vbox, Pos.CENTER);
            vbox.setAlignment(Pos.CENTER);
            title.setFont(new Font(170)); // set to Label
            title.setTextFill(Color.SNOW);
            title.setEffect(new DropShadow());
            subtitle.setFont( new Font (30));
              
        }
    // Solution Code
    public void setVBoxHandler(EventHandler<? super MouseEvent> value){
          vbox.setOnMouseClicked(value); 
     }
    }
    
    

    然后我在start方法中设置handler:

    public void start(Stage primaryStage) throws Exception {
    
            TitleCard root = new TitleCard();
            VBox vBox =(VBox) root.getChildren().get(1);
            root.setVBoxHandler(e->changeSceneToLoginMenu() ); // solution Code
            
            Scene scene = new Scene(root, 1280, 720);
            primaryStage.setScene(scene);
            primaryStage.show();
    
            this.primaryStage = primaryStage;
    
        }
    
        public void changeSceneToLoginMenu() {
            System.out.println("It finally worked");
            Scene currentScene = new Scene(new Group(),100,100); // just a demo
            primaryStage.setScene(currentScene);
    
        }
    

    注意:setVBoxHandler(EventHandler&lt;? super MouseEvent&gt; value) 方法上的值类型将取决于所使用的setOnXXX 方法。例如我测试过,这个灵魂也适用于只需将类型更改为EventHandler&lt;ActionEvent&gt; value的按钮。

    关于“如何使用处理程序”的问题的一些评论发布了有关“如何使用处理程序”的链接,这些帖子使用了异常类。我相信这种方式已经过时了。我在代码中使用了 lambdas,最终结果是相同的,但代码更可重复

    仅供参考,如果未来的读者使用异常类,解决方案将是相同的,只需更改您设置处理程序的方式:

    
    // lambdas
    setVBoxHandler( e -> System.out.println("Code run if mouse is clicked "));
    
    // anomimous classes
    setVBoxHandler(new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent event){
            System.out.println("Code run if mouse is clicked ");
        }
    });
    
    

    【讨论】:

      猜你喜欢
      • 2017-02-28
      • 2015-10-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-16
      • 1970-01-01
      • 1970-01-01
      • 2019-01-01
      • 1970-01-01
      相关资源
      最近更新 更多