Skip to Content
Course content

210: Introduction to JavaFX

Click on the "Edit" button in the top corner of the screen to edit your slide content.

You've spent a lot of time in the console so far, but now we're moving into the visual side of things with JavaFX. If you've ever tried to build a GUI in Java, you know it can feel a bit like magic until something breaks. When it does break, it usually breaks in a way that makes no sense at first glance.

Take a look at this snippet. I've written a simple app that's supposed to simulate a long-running task—like downloading a file—and update a label when it's finished. On the surface, it looks perfectly logical.

public class DownloaderApp extends Application {
    @Override
    public void start(Stage primaryStage) {
        Label statusLabel = new Label("Ready to download...");
        Button startButton = new Button("Start Download");

        startButton.setOnAction(e -> {
            // We start a new thread so the UI doesn't freeze during the "download"
            new Thread(() -> {
                try {
                    Thread.sleep(3000); // Simulate a 3-second download
                    statusLabel.setText("Download Complete!"); 
                } catch (InterruptedException ex) {
                    ex.printStackTrace();
                }
            }).start();
        });

        VBox root = new VBox(10, statusLabel, startButton);
        primaryStage.setScene(new Scene(root, 300, 200));
        primaryStage.show();
    }

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

The dreaded Not on FX application thread error

If you run this, the app will launch, the button will click, and then—three seconds later—your console will explode with an IllegalStateException: Not on FX application thread. The program crashes, and the label never updates.

Here is what's happening: JavaFX, like almost every modern GUI toolkit, is single-threaded. There is one specific thread—the JavaFX Application Thread—that owns the "scene graph" (all your buttons, labels, and layouts). If you try to change the text of a label or the color of a button from any other thread, JavaFX throws a fit and shuts the operation down to prevent memory corruption and unpredictable rendering glitches.

I used a new Thread() to prevent the UI from freezing (which is the right instinct), but then I tried to reach back into the UI from that background thread to update the label. That's the forbidden move.

Wrapping UI updates in Platform.runLater()

To fix this, we need a way to tell JavaFX: "Hey, I'm doing work in the background, but when you have a spare millisecond, please run this specific piece of code on your own main thread."

That's exactly what Platform.runLater() does. It takes a Runnable and queues it up for the UI thread to execute as soon as possible.

// Inside the Thread's run method:
new Thread(() -> {
    try {
        Thread.sleep(3000);
        
        // This is the fix: wrap the UI update in Platform.runLater()
        Platform.runLater(() -> {
            statusLabel.setText("Download Complete!");
        });
        
    } catch (InterruptedException ex) {
        ex.printStackTrace();
    }
}).start();

By wrapping the setText() call, we aren't forcing the update from the background thread; we're sending a request to the UI thread to handle it. The background thread keeps running its own business, and the UI thread handles the visual update when it can. This keeps your app responsive and, more importantly, keeps it from crashing.

Structuring your JavaFX Application

Aside from the threading issues, you'll notice a specific structure in JavaFX apps. You don't just start with a main method that does everything. Instead, you extend the Application class and override the start(Stage primaryStage) method.

  • The Stage: Think of this as the window frame. It handles the minimize/maximize buttons and the window title.
  • The Scene: This is the content inside the window. You can swap scenes out (like switching from a login screen to a dashboard) while keeping the same stage.
  • The Scene Graph: This is the hierarchy of nodes (Buttons, Labels, Layouts) that make up your scene. I used a VBox in the example, which simply stacks elements vertically.

When you call launch(args) in your main method, JavaFX handles all the heavy lifting of initializing the toolkit and eventually calling your start method on that precious Application Thread we talked about.




📋 Practical Task

Exercise: Building a Real-Time System Monitor

Your goal is to create a JavaFX application that simulates a system resource monitor. The application should have a ProgressBar and a Label showing the current percentage.

Requirements:

  • Create a window (Stage) with a title "System Monitor".
  • Add a ProgressBar and a Label to the layout.
  • Implement a background thread that starts immediately when the app launches.
  • This thread should "simulate" CPU usage by generating a random double between 0.0 and 1.0 every 500 milliseconds.
  • Use Platform.runLater() to update the ProgressBar progress and the Label text (e.g., "CPU Usage: 45%") based on the random value.
  • Ensure the UI remains responsive while the updates are happening in the background.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.