如何设置在按下 JavaFX 中的任何键盘键时发生的操作?
How to set an action to occur on pressing of any keyboard key in JavaFX?
所以我想为大学视频游戏项目做一个标题菜单。我想显示一条消息,请按任意键继续...然后当用户按下任意键(包括鼠标)时,一种方法会 运行 将舞台设置为下一个菜单。
我post编辑了所有相关代码,但简短版本是:
BackgroundImangeDisplayPane
扩展显示窗格并添加背景图像。
TitleCard
extends BackgroundImangeDisplayPane
添加一个 VBox 和 2 个标签到 VBox
- 我主要使用
public void start(Stage primaryStage) throws Exception
,我在这里设置了setOnActionxxx
方法
我已经尝试在 root 和 Vbox 上使用 set on action 方法,但它们都不起作用...当我单击时没有任何反应...但是当我调整 window root.setOnActionXXX
的大小时“激活”。
如果我在 TitleCard
class 上编写 setOnAction 方法,它可以工作,但我无法切换舞台。
我将 post 下面的代码以及对场景结构的解释,它并不复杂:
// 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;
}
我设法找到了一个非常简单的工作解决方案,但我不完全理解它为什么有效。我注意到 如果我在同一个 class 中设置处理程序,则节点被实例化,处理程序将正常工作 但是如果我试图通过主函数的方法获取节点通过 root.getChildren().get(1)
然后将其转换为 VBox 元素,处理程序将不起作用。
作为解决方案我将 VBox 设为一个字段 并为 TitleCard 中的 VBox 事件处理程序编写了一个 setter 方法 Class。这解决了问题。
I marked the code added as solution code with comments
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);
}
}
然后我在启动方法中设置处理程序:
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<? super MouseEvent> value)
上的值类型将取决于所使用的 setOnXXX
方法。例如,我测试过,这个灵魂也适用于只需要将类型更改为 EventHandler<ActionEvent> value
.
的按钮
Some coments on the question posted links on "how to use handlers", this posts used anomimous classes. I belived This way is outdated. I used lambdas in the code the end result is the same but more redable code
仅供参考,如果未来的读者使用异常 classes 解决方案将是相同的,只需更改设置处理程序的方式即可:
// 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 ");
}
});
正如 James 在评论中提出的那样,当 key is pressed on the scene 时,导航到下一个场景(或替换当前场景中的根,并删除按键处理程序)。
scene.setOnKeyPressed(e -> navigateToNextScene());
所以我想为大学视频游戏项目做一个标题菜单。我想显示一条消息,请按任意键继续...然后当用户按下任意键(包括鼠标)时,一种方法会 运行 将舞台设置为下一个菜单。
我post编辑了所有相关代码,但简短版本是:
BackgroundImangeDisplayPane
扩展显示窗格并添加背景图像。TitleCard
extendsBackgroundImangeDisplayPane
添加一个 VBox 和 2 个标签到 VBox- 我主要使用
public void start(Stage primaryStage) throws Exception
,我在这里设置了setOnActionxxx
方法
我已经尝试在 root 和 Vbox 上使用 set on action 方法,但它们都不起作用...当我单击时没有任何反应...但是当我调整 window root.setOnActionXXX
的大小时“激活”。
如果我在 TitleCard
class 上编写 setOnAction 方法,它可以工作,但我无法切换舞台。
我将 post 下面的代码以及对场景结构的解释,它并不复杂:
// 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;
}
我设法找到了一个非常简单的工作解决方案,但我不完全理解它为什么有效。我注意到 如果我在同一个 class 中设置处理程序,则节点被实例化,处理程序将正常工作 但是如果我试图通过主函数的方法获取节点通过 root.getChildren().get(1)
然后将其转换为 VBox 元素,处理程序将不起作用。
作为解决方案我将 VBox 设为一个字段 并为 TitleCard 中的 VBox 事件处理程序编写了一个 setter 方法 Class。这解决了问题。
I marked the code added as solution code with comments
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);
}
}
然后我在启动方法中设置处理程序:
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<? super MouseEvent> value)
上的值类型将取决于所使用的 setOnXXX
方法。例如,我测试过,这个灵魂也适用于只需要将类型更改为 EventHandler<ActionEvent> value
.
Some coments on the question posted links on "how to use handlers", this posts used anomimous classes. I belived This way is outdated. I used lambdas in the code the end result is the same but more redable code
仅供参考,如果未来的读者使用异常 classes 解决方案将是相同的,只需更改设置处理程序的方式即可:
// 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 ");
}
});
正如 James 在评论中提出的那样,当 key is pressed on the scene 时,导航到下一个场景(或替换当前场景中的根,并删除按键处理程序)。
scene.setOnKeyPressed(e -> navigateToNextScene());