Reputation: 75
Ater some googling, I figured that I can change scenes while remaining in the same stage by assigning this method to a button:
Parent root = FXMLLoader.load(getClass().getResource("view/bambam"));
Stage stage= (Stage) ((Node) event.getSource()).getScene().getWindow();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
I don't understand the second line though. What does the stage and node in parenthesis mean? Is it some type of casting? Does it refer to the "primary" stage somehow? I'll be very thankful if someone could fully explain this line or tell me what material I've missed.
Upvotes: 2
Views: 2838
Reputation: 209339
The syntax in which the type is placed in parentheses ahead of the expression is a downcast, which is explained nicely here and here. If you expand the code, it might be clearer:
Parent root = FXMLLoader.load(getClass().getResource("view/bambam"));
// get the source of the event
Object eventSource = event.getSource();
// the event only knows its source is some kind of object, however, we
// registered this listener with a button, which is a Node, so we know
// the actual runtime type of the source must be Button (which is a Node)
// So tell the compiler we are confident we can treat this as a Node:
Node sourceAsNode = (Node) eventSource ;
// get the scene containing the Node (i.e. containing the button):
Scene oldScene = sourceAsNode.getScene();
// get the window containing the scene:
Window window = oldScene.getWindow();
// Again, the Scene only knows it is in a Window, but we know we specifically
// put it in a stage. So we can downcast the Window to a Stage:
Stage stage = (Stage) window ;
// Equivalently, just omitting all the intermediate variables:
// Stage stage= (Stage) ((Node) event.getSource()).getScene().getWindow();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
IMHO this is pretty badly written code. Firstly, downcasting to some extent sidesteps the usual compile-time type checks, relying on our own analysis of our coding logic to avoid runtime exceptions. Thus it is good practice to avoid it when possible.
In this case, you state that this code is part of a handler registered with a button. Therefore, the source of the event is the button. So, instead of going through all those steps to get back a reference to the button, we can just use an existing reference. In other words, you have something like:
button.setOnAction(event -> {
Parent root = FXMLLoader.load(getClass().getResource("view/bambam"));
Stage stage= (Stage) ((Node) event.getSource()).getScene().getWindow();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
});
Here ((Node) event.getSource())
must be the button
, so you can immediately simplify to
button.setOnAction(event -> {
Parent root = FXMLLoader.load(getClass().getResource("view/bambam"));
Stage stage = (Stage) button.getScene().getWindow();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
});
Secondly, there's really no need to replace the Scene
at all: why not just replace the root
of the existing Scene
? For this you can do
button.setOnAction(event -> {
Parent root = FXMLLoader.load(getClass().getResource("view/bambam"));
Scene scene = button.getScene();
scene.setRoot(root);
});
(clearly the Stage
is already showing, since the user clicked on the button, so stage.show()
is redundant).
If you prefer, you can simplify that to
button.setOnAction(event -> {
Parent root = FXMLLoader.load(getClass().getResource("view/bambam"));
button.getScene().setRoot(root);
});
or even just
button.setOnAction(event ->
button.getScene().setRoot(FXMLLoader.load(getClass().getResource("view/bambam"))));
Upvotes: 3