In this article, we’ll discuss how to switch between between multiple scenes in JavaFX.
Once you’ve graduated from creating simple GUI’s in JavaFX and are ready to take the next step, creating multiple windows is a topic that will often come up. One way of doing so is to create a new Stage, thus having two windows with different UI elements.
However, there is an alternative. Instead of having multiple windows clutter your screen, just switch the current Scene with another. It’s much simpler than it looks and interesting to learn.
Switching between two Scenes
There isn’t really any new syntax here, just a new concept that’s interesting to learn. Typically we swap between scenes in certain conditions, like maybe a game where a new map is loading, or accessing the “settings” page from a menu etc.
Since we’re focusing more on the layout and contents of the Scene, we’ll bring out the setAlignment()
method. This method takes as Parameter a Pos
(position) object which determines the position of the widgets in the layout. This method isn’t for the scene though, it’s for our Layout object.
The real magic of this code is the use of the setScene()
function. Using this together with the QPushButton widget we can cause our button clicks to change the scene that’s currently being displayed on our stage.
package application; import javafx.application.Application; import javafx.collections.FXCollections; import javafx.geometry.Pos; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.VBox; public class Tutorial extends Application { public static void main(String args[]){ launch(args); } @Override public void start(Stage primaryStage) throws Exception { VBox layout = new VBox(); VBox layout2 = new VBox(); layout.setAlignment(Pos.CENTER); layout2.setAlignment(Pos.CENTER); Scene scene = new Scene(layout, 300, 300); Scene scene2 = new Scene(layout2, 300, 300); Label label1 = new Label("This is the First Scene"); Label label2 = new Label("This is the Second Scene"); Button button = new Button("Forward"); button.setOnAction(e -> primaryStage.setScene(scene2)); Button button2 = new Button("Backwards"); button2.setOnAction(e -> primaryStage.setScene(scene)); TextField text = new TextField(); text.setMaxWidth(100); layout.getChildren().addAll(label1, button); layout2.getChildren().addAll(label2, button2, text); primaryStage.setTitle("CodersLegacy"); primaryStage.setScene(scene); primaryStage.show(); } }
It’s necessary for each Scene to have it’s own unique layout. You cannot use the same layout object for both Scenes. Similarly, only add those objects into the layout that you want to see on that specific scene.
Below is the output of the above code. Using Pos.CENTER
center-aligned all the objects. You can find a complete list of alignment options here.
Notice that the contents of the TextField remain the same even after the Scene is changed. It’s just something interesting to note.
This marks the end of the Switch between Scenes in JavaFX article. Any suggestions or contributions for CodersLegacy are more than welcome. Questions regarding the article content can be asked in the comments section below.
Switching between Scenes (Screens) in JavaFx using the FXML
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version=«1.0« encoding=«UTF-8«?> | |
<?import javafx.scene.control.Button?> | |
<?import javafx.scene.control.Label?> | |
<?import javafx.scene.layout.AnchorPane?> | |
<AnchorPane id=«AnchorPane« prefHeight=«237.0« prefWidth=«448.0« xmlns:fx=«http://javafx.com/fxml/1« xmlns=«http://javafx.com/javafx/8.0.60« fx:controller=«FXMLDocumentController«> | |
<children> | |
<Button fx:id=«btn2« layoutX=«159.0« layoutY=«126.0« mnemonicParsing=«false« onAction=«#handleButtonAction« text=«Click to go to scene 1« /> | |
<Label layoutX=«179.0« layoutY=«71.0« text=«You re in scene 2« /> | |
</children> | |
</AnchorPane> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version=«1.0« encoding=«UTF-8«?> | |
<?import javafx.scene.control.Button?> | |
<?import javafx.scene.control.Label?> | |
<?import javafx.scene.layout.AnchorPane?> | |
<AnchorPane id=«AnchorPane« prefHeight=«200« prefWidth=«320« style=«-fx-background-color: red;« xmlns=«http://javafx.com/javafx/8.0.60« xmlns:fx=«http://javafx.com/fxml/1« fx:controller=«FXMLDocumentController«> | |
<children> | |
<Button fx:id=«btn1« layoutX=«81.0« layoutY=«121.0« mnemonicParsing=«false« onAction=«#handleButtonAction« text=«Click Here To goto scene 2« /> | |
<Label layoutX=«122.0« layoutY=«55.0« prefHeight=«14.0« prefWidth=«77.0« text=«This is Scene 1« /> | |
</children> | |
</AnchorPane> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.net.URL; | |
import java.util.ResourceBundle; | |
import javafx.event.ActionEvent; | |
import javafx.fxml.FXML; | |
import javafx.fxml.FXMLLoader; | |
import javafx.fxml.Initializable; | |
import javafx.scene.Parent; | |
import javafx.scene.Scene; | |
import javafx.scene.control.Button; | |
import javafx.scene.control.Label; | |
import javafx.stage.Stage; | |
public class FXMLDocumentController implements Initializable { | |
@FXML | |
private Label lbl1,lbl2; | |
@FXML | |
private Button btn1,btn2; | |
@FXML | |
private void handleButtonAction (ActionEvent event) throws Exception { | |
Stage stage; | |
Parent root; | |
if(event.getSource()==btn1){ | |
stage = (Stage) btn1.getScene().getWindow(); | |
root = FXMLLoader.load(getClass().getResource(«FXML2.fxml»)); | |
} | |
else{ | |
stage = (Stage) btn2.getScene().getWindow(); | |
root = FXMLLoader.load(getClass().getResource(«FXMLDocument.fxml»)); | |
} | |
Scene scene = new Scene(root); | |
stage.setScene(scene); | |
stage.show(); | |
} | |
@Override | |
public void initialize(URL url, ResourceBundle rb) { | |
// TODO | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import javafx.application.Application; | |
import javafx.fxml.FXMLLoader; | |
import javafx.scene.Parent; | |
import javafx.scene.Scene; | |
import javafx.stage.Stage; | |
public class TwoScene extends Application { | |
@Override | |
public void start(Stage stage) throws Exception { | |
Parent root = FXMLLoader.load(getClass().getResource(«FXMLDocument.fxml»)); | |
Scene scene = new Scene(root); | |
stage.setScene(scene); | |
stage.show(); | |
} | |
public static void main(String[] args) { | |
launch(args); | |
} | |
} |
In this article, we show how to create multiple scenes and switch
between scenes in JavaFX.
In JavaFX, an application can only have one stage but that stage
can have 1 or several scenes.
Therefore, we can create multiple scenes for a given JavaFX application.
In this program, we will create 2 scenes and show how to switch
between the scenes in our code.
So the Java code is shown below.
So just like any JavaFX application, you need to import several packages. You’re more than likely familiar with this.
We then create a class that extends the Application. Therefore, we inherit all the functionality of the Application class, which has a lot of classes and methods that we need to use.
You want to declare all your scenes before the start() method. Since we create 2 scenes in this application, we create 2 scenes, scene1 and scene2.
We then write our start() function.
We set the title to «My First JavaFX GUI».
We then create scene1.
To scene 1, we have a label, label1, with the text, «This is the first scene».
We have a button, button1, with the text, «Go to scene 2».
We then add event handling to button1, so that when the button is clicked, we can go to another scene. When button1 is clicked, we set the scene to scene 2. This way, we are able to
switch from one scene to another. This expression is a lambda expression which allows for easy event handling in JavaFX.
We then create the layout for the window. VBox is a layout in which the elements are stacked vertically on top of each other. We pass in the parameter, 20, so that the elements are spaced
20px apart (so that they’re stacked directly on top of each other).
We then take the layout and add hte label1 and button1 elements. Again, these are stacked vertically, not on the same line. If you wanted them on the same line, you could use
HBox (standing for horizontal box).
We then initialize the scene. We set the layout and the width and height.
We then go on to create scene2.
Basically, to make this short, we are all the same elements, a label and a button as before. However, to the button, we attach event handling so that when the button is clicked, it
goes to scene 1. This way, we are able to switch to scene 1 on the scene 2 page and switch to scene 2 on the scene 1 page.
You can add as many scenes as you want to.
Being that we have created 2 scenes, you have to specify which scene is shown first.
We, therefore, set the primaryStage to scene1 through the setScene() function.
We then show the window through the line, primaryStage.show();
This way, we are able to show the window.
And this is all that is required to create multiple scenes and switch between the scenes. All that is necessary is to add event handling to an element such as a button in a scene
and use the setScene() function to specify which scene you would like to go to.
Related Resources
One of the first things you have to do when you create your first JavaFX app is to create a Scene
. If you’re anything like me when I first started, you might just blindly slot it into something called a Stage
, and maybe set a width and height. Secretly, you’re not really sure what either the scene or the stage are doing.
That’s OK. I made this article s that you don’t have to fumble around in the source code and docs like I did.
The Scene is the highest element of the JavaFX scene graph, containing all of the nodes visible in a window. A Scene can be created without a Stage, but won’t be visible. A scene can be associated with only one Stage at a time.
The scene must be created with a Parent (a layout or a group), which holds the contents of the scene graph.
What will you get from this tutorial?
I’ll go through the important parts about creating, modifying and efficiently using a Scene
. There are a lot of ways to use a Scene
– some wiser, some slightly more avant garde. My goal is to show you the good ways.
In total, I’ll cover five areas:
- What is a Scene, and how to create it
- How to change, close and clear a Scene
- Setting the background color of a scene
- Listening for Scene-level events like keystroke combinations
Towards the end of the article I’ll go through some best practice about how to use the scene (see the last three bullet points).
A Scene
describes everything that is inside a window in a JavaFX application. Where the Window
provides a look and feel that’s specific to the OS that’s driving the application, the Scene
is completely yours to style and change.
The window that most people will use is a Stage
, which extends the Window
class
What is a Stage, Scene Graph and Node?
Each of these concepts – Stage, Scene Graph and Nodes – surround the Scene object, and together they discharge the responsibility of rendering your application to the screen.
Stage
The Stage represents the Window of any view in the JavaFX model. In fact, it extends the Window
Scene Graph
The Scene Graph in JavaFX represents the contents of a scene that focusses on the hierarchical relationship between nodes.
It might be a little dry, but this video from some of the designers ar Sun Microsystems does a pretty good job of talking through the scene graph.
Prism Rendering Scene Graph
Alongside the scene graph we get to see and modify, JavaFX maintains a sister scene graph, and uses this to determine (amongst other things):
- How much of the scene has changed and which areas of the scene need updating
- The textures required to render the scene
- The rendering of the scene
Part of the reason that the JavaFX scene can be so efficient is that it caches the unchanged elements of a scene so they don’t need to be re-computed unnecessarily.
Nodes
Nodes are the foot soldiers of a user interface. From charts to images, layouts to shapes – they all extend the Node class in some way. The node is the basic interface component that has:
- Bounds
- Layout position
- Event handling (moose, keystoke, etc)
- Opacity, and
- Transforms
All other UI elements layer functionality on top of the node class.
The Scene is responsible for four things:
- Holding a reference to the Parent that holds the scene’s nodes (this is most often a layout of some type, but can be a Group).
- Optionally, controlling the size of the window (this can be delegated to the root node, but doesn’t have to be)
- Setting default higher-level rendering properties (useful for 3D scenes), and the camera (also for 3D)
- Triggering event handling behind the scenes
Each of these things impacts how the Scene
works, and how it needs to be defined. For instance, because the Scene
will hold your scene graph, it must to be defined with a root node.
Antialiasing and depth testing correspond to higher-level rendering properties, which can be set as defaults for the scene.
The only aspect of the scene I won’t talk about here is events. I’ll talk about how to use events in the scene (see the last section for that), but not how the Scene itself dispatches events. Events in JavaFX are a such a fundamental topic, I wanted to create a resource that covered them in more depth.
If you want to know more about events, and the Scene’s role in them, check out my article, which takes you through absolutely everything you need to know when it comes to events.
Creating a Scene
Because the Scene
is responsible for maintaining the scene graph, it must be created with a Parent
object, called the “root”, which contains all nodes in the scene.
Scene(Parent root);
Optionally, it can be created with a width, height and background color:
Scene(Parent root, double width, double height); Scene(Parent root, double width, double height, Paint fill); Scene(Parent root, Paint fill);
Finally, and importantly for 3D scenes, the default antialiasing and depth testing properties if a Scene can be defined at creation:
Scene(Parent root, double width, double height, boolean depthBuffer); Scene(Parent root, double width, double height, boolean depthBuffer, SceneAntialiasing antiAliasing);
How to change, close and clear a Scene
Once your application is up and running, you might need to change the contents of your Scene, close it, or clear it completely! Each of these can be done in several ways, so I’ll take you through each.
How to change a scene
Moving users on from one screen to the next is a common use case for changing the contents of a Scene without changing the window. Especially if the user is working through a multi-page form, the user expectation is that they’ll stay in the same window.
You can accomplish this in a couple of ways, but they essentially all boil down to replacing one part of the scene graph with another. This can be done as high as the root node, or the Scene itself, but doesn’t have to be.
Changing the entire Scene
To remove the entire scene from the window and replace it with different content, invoke setScene()
on the Stage object that is currently holding it. Replace the scene with a new one to replace the content.
stage.setScene(newScene);
Bear in mind because you’re replacing the entire Scene object, you’ll also get rid of any event listeners you’ve previously installed. If you don’t want to get rid of the listeners, you might want to consider switching out the root node instead.
Switching out the Root node
Replacing the root node will leave all of your event listeners intact on the current scene, but will still achieve the result of replacing all of the visible contents.
Re-setting the root node can be accomplished by invoking setRoot()
on the Scene object with the new Parent
object as an argument.
scene.setRoot(newRoot);
Smoother transitions – sliding
Finally, you can go even further down the scene graph to to achieve a smoother transition. By keeping the root node and switching out the contents of the rest of the scene, you can create really attractive transitions, such as sliding.
Closing a window
I’ve included this for completeness here, but it’s worth saying that ‘closing a scene’ is a bit of a misnomer. Closing a scene is a Window-level action. That means it has to be invoked on the Window
object, rather the Scene
object.
With that in mind, I will say that when I first started with JavaFX, I hadn’t worked through distinction between scene and stage. For that reason, I’d often search around trying to work out how to close a scene.
To close the window, invoke getWindow()
to access the current window, and invoke hide()
on the window object returned.
textField.getScene().getWindow().hide();
Setting the background color of a scene
The default background color of the Scene object is white, but you might have noticed that whenever you create a scene, it looks a light grey color. That’s because the root node also gets styled. And – because the root note is usually a layout pane – it usually gets pulled to fill the entire scene.
Styling the root node
The root node’s style is set using the CSS style class “root”. Modern versions of JavaFX ship with the default stylesheet Modena.css. Here’s a link to the stylesheet that shipped with JavaFX 15: Modena.css. And here’s the excerpt that’s relevant to the background color:
.root { /*************************************************************************** * The main color palette from which the rest of the colors are derived. * **************************************************************************/ -fx-base: #ececec; -fx-background: derive(-fx-base,26.4%); ... } ...
In terms of background color, that means the default background color is a very light grey (exciting stuff). You can either set the background by creating a Background
object, or by styling it with CSS.
I’d always suggest linking your FXML with a style sheet, but for brevity, here’s how to do it in Java code.
root.setStyle("-fx-background-color: #81c483");
If you’re planning on styling your layout with a linked CSS style sheet, remember that layout panes don’t have a default style class. You can add one in Java code, or in FXML. Here’s a quick example with a BorderPane (don’t forget to link the stylesheet).
BorderPane borderPane = new BorderPane(); borderPane.getStyleClass().add("border-pane"); borderPane.getStylesheets().add("mystyles.css");
.border-pane{ -fx-background-color: #81c483; }
Set the color of the Scene
If you’ve made your layout transparent – or if your root node is a Group, you might still want to color the Scene directly. That’s as simple as passing a color into the setFill()
method.
scene.setFill(Color.web("#81c483"));
As a reminder, you won’t be able to see the color of the scene if you’ve got a layout that stretches to fit it. I’ve forgotten this a hundred times over the years, so it bears repeating.
How to get and set the size of a scene
Resizing a scene is actually wickedly simple to do, you just can’t do it with the Scene
object. You can’t change the size of the scene because it’s fundamentally tied to being within the stage.
So, to change the size of a Scene, you just need to find the window it’s fitted into, and change the size of that window instead. This will pull the Scene
into shape, because the scene will always expand to fill it’s window.
Window window = scene.getWindow(); window.setWidth(450); window.setHeight(150);
The Scene object itself has accessor methods for the width and height, as well as providing access to them as properties. Just bear in mind you’ll get them as read-only properties, so you can’t change it on the sly.
Scene scene = new Scene(root); double width = scene.getWidth(); double height = scene.getHeight(); ReadOnlyDoubleProperty wProperty = scene.widthProperty(); ReadOnlyDoubleProperty hProperty = scene.heightProperty();
Listening for Scene-level events like keystroke combinations
You can listen for events like mouse movement and key strokes with event handlers and filters, just as you would on a node.
addEventHandler(EventType<T> eventType, EventHandler<? super T> eventHandler); addEventFilter(EventType<T> eventType, EventHandler<? super T> eventFilter);
Listening for events at the Scene level (rather than on individual nodes) also has substantial benefits. Applying an Event Filter to the Scene
absolutely ensures that whatever functionality we want to happen is immediately completed (rather than waiting for other events to fire first).
If you’re wondering why, take a look at my article on Event Filters. It goes through all of the detail on why event filters fire first, and best practice for using them. In this case, we’ll check for the key combination Ctrl+S.
One of the most common use cases for listening to a Scene is core window user expectations such as Open, Save, and Exit. They’re usually represented by commonly-understood key strokes like Ctrl+S or Command-S on Mac.
KeyCombination save = new KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN); scene.addEventFilter( KeyEvent.KEY_PRESSED, event -> { if(save.match(event)) saveSession(); } );
Because we’re using an event filter (rather than an event handler) you can also chose whether you want the event to propagate throughout the rest of the scene’s nodes. If you don’t, you just need to invoke event.consume()
after you’ve taken whatever action you need.
Of course, if you want to add an event that will fire after every other event in the Scene, you can do so by adding event handlers.
Conclusions
The Scene is one of the most fundamental parts of JavaFX, but often gets overlooked. It holds a reference to the lower scene graph that can be modified or changed, enabling you to swap out scenes as and when required.
Setting the background color of the scene is usually best accomplished by styling the root node. Like the skin of a drum, layouts are stretched to fit the scene, so usually mask the scene’s actual color. Where the layout is transparent, or the root node is a Group
, the scene’s background color can be set too.
Finally, event handling on scenes is a powerful way to enforce action at the very start and end of the event dispatch chain. Listening for keystrokes across the scene can be a highly useful way of ensuring user actions such as save and open commands are acted on – regardless of which node currently has focus.