Java @ Kiowok

Graphical User Interface (GUI) Programming with JavaFX

The programs presented so far use either the console or JOptionPane-based dialog boxes to communicate with the user. In this section we'll explore the use of custom graphical user interfaces (GUIs), which can be customized to hold any combination of GUI components, such as buttons, text fields, scroll bars, check boxes, etc. Previous GUI architectures in Java were AWT and Swing. In these notes we will look at the most recent Java GUI architecture, JavaFX.

You can implement a JavaFX program in several ways. Here we'll look at developing all of the user interface and event handling in Java code, using lambda expressions in the event handlers (these terms will make more sense shortly).

Creating the User Interface

The JavaFX system uses a theater metaphor to describe the user interface. The main window is a Stage, inside the stage you setup a Scene. The individual components you see (e.g., Buttons, TextFields, Sliders) would then be the actors on on the scene. A scene can only hold one thing, so if you want to have multiple components (Buttons, TextFields, etc.) in a scene you need to put them into a single container and put that container in the scene. A container is an invisible structure whose purpose is to other visual components, and arrange the components in a given order, such as horizontally, vertically, or in a grid.

It is recommended to use a professional IDE such as NetBeans, eclipse, or IntelliJ to write JavaFX programs, as these IDEs can save you a lot of typing.

In the steps that follow we'll create a GUI that shows the user a button with the word "Click" on it, and an empty text field. The user can type whatever they like in the text field:

Window with a button labeled "Click" and a text field holding the text "apples".

but when the user presses the "Click" button the text in the text field changes to "I was clicked!":

The same window as just seen above, but with the word "apple" changed to the words "I was clicked!".

Here is a typical starting point for a JavaFX application:

package clickme;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ClickMe extends Application {
    @Override
    public void start(Stage primaryStage) {

    }

    public static void main(String[] args) {
        launch(args);
    }
}

The application will starting in the main method, which launches the GUI system. Part of the launch process includes a call to the start method, which is handed an empty Stage (the stage here is called primaryStage). All of the code we'll write here will be in the start method, where we'll:

Build the GUI Steps

  1. Create a Button named click.
  2. Create a TextField named text.
  3. Create an invisible container of type HBox (called box) that can hold the Button and the TextField and do what is necessary to present them horizontally.
  4. Put the button and the text field into the HBox.
  5. Create a Scene named scene to hold the HBox (which holds the button and text field).
  6. Put the HBox in the scene.
  7. Put the scene on the Stage called primaryStage.
  8. Show the stage and its contents to the user.

Once the steps above have been performed, then the user interface is completed, but we will still need to implement the event handling.

The starting code above gives us an empty stage:

The stage is an empty box to begin with.

We are going to write the code to accomplish the eight steps outlined in the "Build the GUI Steps" above. The first seven steps are show visually below:

This diagram shows the creation of the button and the text field, and the HBox.  The button and the text field are placed in the HBox, which is then placed in the scene, and the scene is then placed on the stage.

Here is the corresponding code:

package clickme;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ClickMe extends Application {
    @Override
    public void start(Stage primaryStage) {
        Button click = new Button("Click"); // Step 1
        TextField text = new TextField("apples"); // Step 2
        HBox box = new HBox();  // Step 3
        box.getChildren().addAll(click, text); // Step 4
        Scene scene = new Scene(box);  // Steps 5 and 6
        primaryStage.setScene(scene); // Step 7
        primaryStage.setTitle("Click Me!");
        primaryStage.show(); // Step 8 
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Note the new import statements from the javafx package for Button, TextField, etc.

When the above code is run, the user will see the following window:

Window with a button labeled "Click" and a text field holding the text "apples".

where the user will see a "Click" button and a TextField holding the word "apples". The user can click the button, but nothing will happen yet. The user can enter any text into the text field.

Event Handling

The following instructions will add event handling to the application, so that when the user clicks the "Click" button the text in the text field changes to "I was clicked!".

Anytime after the button click has been created you can attached it to a chunk of code that will be performed each time the button is clicked. To attach code to a button you call the setOnAction method, and specify the code to be run in the parenthesis for setOnAction's parameter list:

package clickme;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ClickMe extends Application {
    @Override
    public void start(Stage primaryStage) {
        Button click = new Button("Click"); // Step 1
        TextField text = new TextField("apples"); // Step 2
        
        click.setOnAction(    
                (event)-> {
                    // any code written here is perform every time the click button is clicked
                }
        );
        
        HBox box = new HBox();  // Step 3
        box.getChildren().addAll(click, text); // Step 4
        Scene scene = new Scene(box);  // Steps 5 and 6
        primaryStage.setScene(scene); // Step 7
        primaryStage.setTitle("Click Me!");
        primaryStage.show(); // Step 8 
    }

    public static void main(String[] args) {
        launch(args);
    }
}

The code inside setOnAction's parameter is (event)->{}. Code structured this way is known as a lambda expression, and it is actually a shorthand for a more complicated structure known as an anonymous inner class. We won't cover anonymous inner classes here -- just keep in mind that all you have to do is put your event handling code (the code you want to have performed each time the click button is clicked) inside the {}s of the lambda expression.

The event handling code is supposed to set the text in the text TextField to "I was clicked!". To do that, add the following code inside the lamdba expression:

package clickme;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ClickMe extends Application {
    @Override
    public void start(Stage primaryStage) {
        Button click = new Button("Click"); // Step 1
        TextField text = new TextField("apples"); // Step 2
        
        click.setOnAction(
                (event)-> {
                    text.setText("I was clicked!");
                }
        );
        
        HBox box = new HBox();  // Step 3
        box.getChildren().addAll(click, text); // Step 4
        Scene scene = new Scene(box);  // Steps 5 and 6
        primaryStage.setScene(scene); // Step 7
        primaryStage.setTitle("Click Me!");
        primaryStage.show(); // Step 8 
    }

    public static void main(String[] args) {
        launch(args);
    }
}

When the user clicks the "Click" button you should see the following:

The text field's content has changed to "I was clicked!"

Another Sample Application - Slider Demo

Here we'll create a JavaFX application that presents the user with a slider and a label. Whenever the user moves the slider's thumb, the thumb's position is shown on in the label.

This video follows along with the material covered in this section. The video has both closed captions and a transcript:

Initially, the user is presented with the slider's thumb at the far left, which is position 0.0:

SilderDemo app with the thumb at position 0.0.

By default the range is 0.0 when the thumb is pulled all the way to the left, and 100.0 iwhen the thumb is pulled to the far right.

When the user slides the thumb, the new position is reported. Here, the thumb has been moved to position 73.01...

SilderDemo app with the thumb at position 73.01.

The following structures must be built in memory to create this visual interface:

Memory architecture of the SliderDemo app.

This structure is very similar to the one built in the first application shown on this web page. Here we use a VBox instead of an HBox to hold the individual components. A VBox presents the components vertically, versus the horizontal presentation of an HBox.

The following code creates this GUI in memory, sets up the event handler, and displays the GUI to the user:

package sliderdemo;

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class SliderDemo extends Application {   
    @Override
    public void start(Stage primaryStage) {
        Slider slider = new Slider();
        Label label = new Label("Label: 0");
        
        slider.valueProperty().addListener(
                (ov, oldValue, newValue)->{
                    label.setText("Value: " + slider.getValue());   
                }
        );
        
        VBox vbox = new VBox();
        vbox.getChildren().addAll(slider, label);
        Scene scene = new Scene(vbox);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Notice that the event handler for the Slider has a different setup than what we used for the Button above. For the button we used setOnAction to connect the button to its event code. Here we use valueProperty().addListener to connect the slider to its event code:

valueProperty().addListener connects the slider to the event handling code.

The slider's event handling code is handed three values, here with the names ov, oldValue and newValue, where oldValue is the position the thumb has just moved from, and the newValue is the position the thumb has just been moved to.

Every time the slider's thumb is moved, this instruction in the event handler

label.setText("Value: " + slider.getValue());

is performed to get the slider's current position and to write that position to the label.

Top