Stage
s and Scene
sStage
The Stage
class represents the window of a GUI application. On Windows, it will appear with a blue bar with sizing controls in the upper right (aka, a standard Windows window). On OS X, it appears as below:
When you write a JavaFX application, a Stage
is automatically created for you; this is where the primaryStage
we've been using in our start()
functions comes from. As all other Java objects, Stage
provides us some methods to use.
Note: While many are called, few are chosen. We will be using a subset of Stage
's methods
Method | Description |
---|---|
void close() | Closes the stage |
void initModality(Modality) | If called before show() , will make the stage a modal |
double getMaxHeight() | Get the max height for the stage |
double getMaxWidth() | Get the max width for the stage |
double getMinHeight() | Get the min height for the stage |
double getMinWidth() | Get the min width for the stage |
void setFullscreen(boolean) | Sets the fullscreen status of the stage |
void setMaximized(boolean) | Sets the maximized status of the stage |
void setMaxHeight(double) | Sets the max height of the stage |
void setMaxWidth(double) | Sets the max width of the stage |
void setMinHeight(double) | Sets the min height of the stage |
void setMinWidth(double) | Sets the min width of the stage |
void setResizable(boolean) | Sets the resizability of the stage |
void setScene(Scene) | Sets the scene to be displayed |
void setTitle(String) | Sets the title of the stage |
void show() | Makes the stage visible |
void showAndWait() | Makes the stage visible and waits until closing to continue |
void toFront() | Forces the stage to the foreground |
void toBack() | Forces the stage to the background |
Scene
Scene
s are containers for GUI elements, like a layout pane or a Button
. They are also used to generally set the size of the Stage
containing them. Let's look at the methods we'll be using from Scene
:
Method | Description |
---|---|
Scene(Parent) | Creates a Scene with a Parent as the root node |
Scene(Parent, double, double) | Creates a Scene with a specified width and height |
double getHeight() | Gets the height of the scene |
double getWidth() | Gets the width of the scene |
double getX() | Gets the horizontal position of the scene |
double getY() | Gets the vertical position of the scene |
void setRoot(Parent) | Sets the root node |
Scene
sEach Stage
can only display one Scene
at a time. However, we have the setScene(Scene)
method in Stage
, which allows us to change the displayed Scene
. Let's see how we can use multiple Scene
s by creating an application that combines the two previous (ClickCounter and AddSubtract):
import javafx.application.*;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;
public class SceneSwitcher extends Application
{
// Elements of ClickCounter scene
int clickCount = 0;
Label lblClicks;
Button btnClickMe;
Button btnSwitch2;
Scene scene1;
// Elements of AddSubtract scene
int count = 0;
Label lblCounter;
Button btnAdd;
Button btnSub;
Button btnSwitch1;
Scene scene2;
// The overall stage
Stage stage;
public static void main(String[] args)
{
launch(args);
}
@Override
public void start(Stage primaryStage)
{
stage = primaryStage;
// Build the ClickCounter scene
lblClicks = new Label();
lblClicks.setText("You have not clicked the button.");
btnClickMe = new Button();
btnClickMe.setText("Click me please!");
btnClickMe.setOnAction(e -> btnClickMe_Click());
btnSwitch2 = new Button();
btnSwitch2.setText("Switch!");
btnSwitch2.setOnAction(e -> btnSwitch2_Click());
VBox pane1 = new VBox(10);
pane1.getChildren().addAll(lblClicks, btnClickMe, btnSwitch2);
scene1 = new Scene(pane1, 250, 150);
// Build the AddSubtract scene
lblCounter = new Label();
lblCounter.setText(Integer.toString(count));
btnAdd = new Button();
btnAdd.setText("Add");
btnAdd.setOnAction(e -> btnAdd_Click());
btnSub = new Button();
btnSub.setText("Subtract");
btnSub.setOnAction(e -> btnSub_Click());
btnSwitch1 = new Button();
btnSwitch1.setText("Switch!");
btnSwitch1.setOnAction(e -> btnSwitch1_Click());
HBox pane2 = new HBox(10);
pane2.getChildren().addAll(lblCounter, btnAdd, btnSub, btnSwitch1);
scene2 = new Scene(pane2, 300, 75);
primaryStage.setScene(scene1);
primaryStage.setTitle("Scene Switcher");
primaryStage.show();
}
// ClickCounter event handlers
private void btnClickMe_Click()
{
clickCount++;
if (clickCount == 1) {
lblClicks.setText("You have clicked once.");
} else {
lblClicks.setText("You have clicked " + clickCount + " times.");
}
}
private void btnSwitch2_Click()
{
stage.setScene(scene2);
}
// AddSubtract event handlers
private void btnAdd_Click()
{
count++;
lblCounter.setText(Integer.toString(count));
}
private void btnSub_Click()
{
count--;
lblCounter.setText(Integer.toString(count));
}
private void btnSwitch1_Click()
{
stage.setScene(scene1);
}
}
Sometimes your application may need to display small dialog boxes to display information to the user, or even to gather information from the user. Earlier Java GUI APIs had built in modal dialog boxes, however JavaFX does not. So, let's make one to use! We can then use this dialog, or message, box elsewhere.
Create a new Java package with the following name:
edu.govschool.modals
The code will look like this:
package edu.govschool.modals;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;
import javafx.geometry.*;
/**
* Displays a modal dialog box for messages.
* @author Bryce Davis
*/
public class MessageBox {
/**
* Show a <code>MessageBox</code>.
* Display a modal dialog box with a message and a title.
* @param msg The message to display
* @param title The title of the message box
*/
public static void show(String msg, String title)
{
// Create a new stage, or window, to display. Since we want a modal
// dialog, we set the modality as APPLICATION_MODAL. We also set the
// minimum width the box can be.
Stage stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
stage.setTitle(title);
stage.setMinWidth(250);
// This label will display the message
Label lbl = new Label();
lbl.setText(msg);
// The button to close the dialog
Button btn = new Button();
btn.setText("OK");
btn.setOnAction(e -> stage.close());
// Position all of the elements vertically
VBox pane = new VBox(20);
pane.getChildren().addAll(lbl, btn);
pane.setAlignment(Pos.CENTER);
// Add everything to the scene and display the box
Scene scene = new Scene(pane);
stage.setScene(scene);
stage.showAndWait();
}
}
You can then use it in other Netbeans projects! Right-click the Libraries
option under the project in the projects view, and then Add Project
. You can choose other Netbeans projects you've created to use as a library.
Let's also create a confirmation box, so the user can make a boolean choice in our applications:
package edu.govschool.modals;
import javafx.application.*;
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.scene.text.*;
import javafx.event.*;
import javafx.geometry.*;
/**
* Displays an OK/Cancel confirmation box.
* Used if the user needs to confirm or deny an action.
* @author Bryce Davis
*/
public class ConfirmationBox {
// These are static because these dialog boxes are considered "singleton"
// objects, or objects that only have one instance. Normally this instance
// is created via a static class method, and is the case here.
static Stage stage;
static boolean confirm;
/**
* Display a confirmation box.
* @param msg The message to display
* @param title The title of the box
* @param yesText The text for the confirm button
* @param noText The text for the deny button
* @return <code>true</code> if the user confirms, <code>false</code> otherwise
*/
public static boolean show(String msg, String title,
String yesText, String noText) {
// We will use this boolean to return the user's choice
confirm = false;
// Again, we are initializing a modal stage
stage = new Stage();
stage.initModality(Modality.APPLICATION_MODAL);
stage.setTitle(title);
stage.setMinWidth(250);
// Create our various GUI elements
Label lbl = new Label();
lbl.setText(msg);
Button confirmBtn = new Button();
confirmBtn.setText(yesText);
confirmBtn.setOnAction(e -> confirmBtn_Click());
Button denyBtn = new Button();
denyBtn.setText(noText);
denyBtn.setOnAction(e -> denyBtn_Click());
// Here is an example of using multiple layout panes to organize our
// elements. The buttons are arranged horizontally in an HBox, but the
// Label and this HBox are arranged vertically in a VBox.
HBox buttonsPane = new HBox(20);
buttonsPane.getChildren().addAll(confirmBtn, denyBtn);
VBox pane = new VBox(20);
pane.getChildren().addAll(lbl, buttonsPane);
pane.setAlignment(Pos.CENTER);
// Let's set the scene!
Scene scene = new Scene(pane);
stage.setScene(scene);
stage.showAndWait();
return confirm;
}
/**
* Event handler for the confirmation button.
* Closes the stage and sets that the user confirmed.
*/
private static void confirmBtn_Click()
{
stage.close();
confirm = true;
}
/**
* Event handler for the deny button.
* Closes the stage and sets that the user denied.
*/
private static void denyBtn_Click()
{
stage.close();
confirm = false;
}
}
Finally, we can have a modal with a custom Scene:
package edu.govschool.modals;
import javafx.scene.*;
import javafx.stage.*;
/**
* Modal to display a custom <code>Scene</code>.
* @author Mr. Davis
*/
public class CustomBox
{
private static Stage stage;
/**
* Show a modal dialog with a custom <code>Scene</code>.
* @param scene the <code>Scene</code> to display
* @param title the title of the modal
*/
public static void show(Scene scene, String title)
{
stage = new Stage();
stage.setScene(scene);
stage.setTitle(title);
stage.showAndWait();
}
/**
* Show a modal dialog with a <code>Parent</code> node as
* the root node.
* @param node the root GUI element
* @param title the title of the modal
*/
public static void show(Parent node, String title)
{
show(new Scene(node), title);
}
}
Stage
So far, we have not been using the best practices for handling the window closing. Even if we tried to be prudent and code an 'Exit/Close' button that takes care of any last-minute operations that need to be performed before closing, if the user simply clicks the Close button in the window's toolbar (generally, this is a red button) then that code won't be run! Instead, we need to listen for and handle CloseRequest
events. As it turns out, Stage
has a method called setOnCloseRequest(EventHandler)
, which accepts an EventHandler
argument. As such, let's use a lambda expression! We'll write an updated version of ClickCounter using our new modals.
Note: Java will automatically close the stage unless the event is 'consumed', see the lambda below
import javafx.application.*;
import javafx.stage.*;
import javafx.scene.*;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.geometry.*;
public class ClickCounter extends Application
{
private static Stage stage;
private static int count = 0;
public static void main(String[] args)
{
launch(args);
}
@Override
public void start(Stage primaryStage)
{
stage = primaryStage;
// Create the buttons
Button clickBtn = new Button();
clickBtn.setText("Click me please!");
clickBtn.setOnAction(e -> clickBtn_Click());
Button closeBtn = new Button();
closeBtn.setText("Close");
closeBtn.setOnAction(e -> closeBtn_Click());
// Organize the elements
VBox pane = new VBox(10);
pane.getChildren().addAll(clickBtn, closeBtn);
pane.setAlignment(Pos.CENTER);
// Add the pane to the scene
Scene scene = new Scene(pane, 250, 150);
// Finish and show the stage.
// Not the lambda expression. The event 'e' must be consumed, else
// Java will close the window regardless of the user's choice.
stage.setScene(scene);
stage.setTitle("Click Counter");
stage.setOnCloseRequest(
e -> {
e.consume();
closeBtn_Click();
}
);
stage.show();
}
public void clickBtn_Click()
{
count++;
if (count == 1) {
MessageBox.show("You have clicked once.", "Click!");
} else {
MessageBox.show("You have clicked " +
count + " times", "Click!");
}
}
public void closeBtn_Click()
{
boolean confirm = ConfirmationBox.show(
"Are you sure you want to quit?", "Confirmation",
"Yes", "No"
);
if (confirm) {
// If we needed to do anything right at the end, like closing a
// file, we do it here.
stage.close();
}
}
}