Issue
So I want to do a title Menu for a video game project for college. I want to display a message press any key to continue... then when the user pressed any key, including the mouse, a method would run to set the stage to the next menu.
I posted all the relevant code bellow but short version is:
BackgroundImangeDisplayPane
extends display pane and adds a background image.TitleCard
extendsBackgroundImangeDisplayPane
adds a VBox and 2 labels to the VBox- I'm using
public void start(Stage primaryStage) throws Exception
as the main, I set thesetOnActionxxx
methods here
I have tried using the set on action method on root and Vbox and non of them work... when I click nothing happens... But when I resize the window The root.setOnActionXXX
"activates".
If I write the setOnAction methods on the TitleCard
class It kind of works but then I cant switch the stage.
I will post the code bellow as well an explanation of the Scene structure its not to complicated:
// 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;
}
Solution
I managed to find a very simple working solution but I don't fully undestand why it does work. I Noticed that if I set the handler in the same class the node was instanciated the handler would work fine But if I tried to get the node with a method to the main fuction via root.getChildren().get(1)
and then cast it to the VBox element the handler would not work.
As a solution I made the VBox a field and wrote a setter method for the VBox event Handler in the TitleCard Class. This fixed the problem.
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);
}
}
Then I set the handler in the start method:
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);
}
notes: The type of value on the method setVBoxHandler(EventHandler<? super MouseEvent> value)
will depend on the setOnXXX
method used. For example I tested and this soultion also works for buttons just need to change the type to 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
Just for reference if future readers are using anomimous classes the solution would be the same just change the way you set up the handler:
// 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 ");
}
});
Answered By - Carlos Martins