When I first started programming with JavaFX, updating the user interface was a constant frustration. An awkward TableView that wouldn’t refresh, or the UI hanging unpredictably. I found out a way to force the Scene to refresh, but in almost every situation it didn’t solve the problem I was having.

The JavaFX scene can be forcibly refreshed by changing the width or height of the Scene by a fractional number of pixels (for example, 0.001). This change forces the background windowing and rendering services to recalculate layout and rendering requirements for the scene graph.

On the face of it, it seems like there are plenty of use cases for forcing a JavaFX scene to refresh. In fact, programmers who’ve used Swing may be used to firing data change events as a way to update their view.

But with JavaFX, in almost all cases, this will not solve the problem you’re having and there are different, more stable, ways to ensure your UI updates like you want it to.

How to force refresh a scene

Refreshing the scene is relatively easy. In the background of JavaFX, the quantum toolkit maintains a sister scene graph, which it uses to calculate parts of the UI that need to be altered. The toolkit then renders them in the rendering pipeline. It uses defined rules to determine which parts of a Scene to re-render.

Fundamentally, if you change something small, the Toolkit will mark the area around it. So, if you change something important at the top of the scene graph – like the width of the scene – no matter how small the change, JavaFX will re-calculate the layout and content of the scene graph completely.

element.getScene().getWindow().setWidth(element.getScene().getWidth() + 0.001);

I can’t stress enough that this is incredibly inadvisable. I say that for three reasons:

1. Stability

Repeatedly doing this at any regular interval threatens the stability of the windowing activities that run in the background. Recalculating the requirements of the scene does not just impact scene-level objects. Above the Scene, the Window will try to accommodate the changes in the Scene. This requires JavaFX to recalculate everything from where to put the window border, to where to put the exit button.

2. Efficiency

Aside from making the window flicker frustratingly, forcing JavaFX to regularly recalculate the rendering requirements for a scene is incredibly inefficient. In the background, JavaFX continually refreshes the scene anyway.

3. It won’t fix your problem

There are a lot of reasons why the JavaFX scene might not refresh, but I guarantee that if your scene isn’t refreshing it isn’t because JavaFX stopped working, or stopped checking whether your scene has changed.

If your scene isn’t refreshing, then forcing it to update will not solve your problem. Changing the scene dimensions might ensure JavaFX knows your scene needs re-rendering. But, if you’re blocking the UI thread, JavaFX won’t have be able to render the changes until you stop what you’re doing.

What’s causing my scene to hang?

As a basic rule, there are two reasons that your scene wouldn’t refresh:

  1. JavaFX doesn’t know about the changes you’ve made.
  2. You’re preventing JavaFX from checking whether to refresh your scene.

We can actually test how frequently JavaFX looks at whether it needs to refresh the scene by registering a listener on the scene’s refresh pulse.

//this variable needs to go in the Main class, outside of the start() method
long lastRefreshTime = 0;

//this goes after you've defined your scene, but before you display your stage
scene.addPreLayoutPulseListener(() -> {
    long refreshTime = System.nanoTime();
    System.out.println(refreshTime - lastRefreshTime);
    lastRefreshTime = refreshTime;
});

This listener will fire every time JavaFX creates a layout pulse. I tracked this over 1000 frames and I’ve plotted the refresh rate (how many times it does it per second) against the pulse number.

You can see JavaFX usually attempts to do this at a rate of 60 frames per second (that’s the almost constant line at the bottom at about 60 Hz). About once every half second, JavaFX fires off some extra pulses at a higher rate (the peaks you can see ranging from 150 to 400 Hz), which is most likely to do with synchronising with other background processes.

The refresh rate isn’t always exactly once every 60th of a second, but it’s pretty evenly distributed around that value. This time I’ve plotted them as a histogram where the heights of the bar represent how frequently the screen refresh rate happens in that range. The height of the blue bar represents all of those synchronisation passes with a higher refresh rate.

The JavaFX scene refresh rate

Really worth noting: this happens even when no changes are being made to your scene. So unless you’re doing something to stop it, at least once every 60th of a second, JavaFX will check whether your scene needs changing, and make those changes if needed.

Let’s take a look at those situations where JavaFX might not refresh your scene.

1. JavaFX doesn’t know about the changes you’ve made

There are some basic rules that govern how JavaFX decides which parts of the scene to refresh.

  1. If a node has changed content (like a text field with changed text), it’s marked to trigger rendering changes.
  2. When a node has changed layout or transform properties, it and its children are marked.
  3. If more than a certain number of grouped nodes are marked, mark the group as dirty. The default threshold is 12.

So, if you think you’ve made a change, and you’ve checked that the scene is refreshing using the Scene’s pre-layout pulse listener, chances are JavaFX doesn’t know about your change. Here are some scenarios where you might get into that situation and how to fix them.

a. You’re trying to update a ListView

A lot of people set up their ListView by wrapping a List they are maintaining with FXCollections.ObservableArrayList(). This creates a copy of the items in the List rather than binding the values together. The ListView is then set up to track changes in the ObservableList but it will not track changes in the original List.

Later, when you update the List. This will not be reflected in the ObservableList, and so the ListView will not know about the changes.

To see how this works, let’s create a list of two Strings and wrap it in an ObservableList. After wrapping, let’s add two items to the List. This is not updated in the ObservableList.

List<String> stringList = new ArrayList<>(Arrays.asList("This", "List"));
ObservableList<String> stringObservableList = FXCollections.observableArrayList(stringList);
stringList.addAll(Arrays.asList("Is", "Longer"));
System.out.println("List: " + stringList);
System.out.println("Observable List: " + stringObservableList);

Solution: Stop maintaining your ArrayList, and start maintaining the ObservableList instead. When you update the ObservableList, your ListView will update automatically This is how JavaFX was intended to operate.

b. You’re changing a custom node in a TableView

In the same way as with a ListView, if you’re simply updating the items in a TableView, you need to do so by maintaining your ObservableList.

But, with a TableView, it’s also possible to define cell factories that completely customise the view of a cell. In this case, if you change the factory or the node so that the layout bounds of the node change (for instance you make the nodes inside the cell bigger), the TableView won’t know to update the bounds of the cell.

In that case, you’ll need to request an update manually by calling refresh() on the TableView object in question. This forces the TableView to recreate the cells necessary to populate the visual bounds of the cells.

2. You’re preventing JavaFX from refreshing the scene.

The most common reason for the UI not refreshing is caused by blocking the UI thread at some point in the program.

Almost always, this is because there’s some long-running process that’s being executed on the Application thread when it could be executed in the background.

What is the Application Thread?

If you haven’t heard the term Application Thread before, it is the primary thread you’ll be coding in while you use JavaFX. Any code you execute to update the user interface – such as setting layout bounds or adding and removing nodes – must be executed on the Application Thread.

Common errors in using the application thread

In the past, I would never want to leave a task running in the background . At first I wasn’t even aware I was stopping the UI from updating. Then, when I’d learned a little more, I still wouldn’t because I wanted to make sure my data made it into the UI as fast as possible.

As a general rule, maintaining the responsiveness of the user interface always trumps the few milliseconds you might save by running the process on the application thread. Here’s a few examples of long-running tasks we sometimes put on the UI thread:

  • Loading from a database
  • Performance-intensive calculations
  • Parsing flat files like text, or csv files.

In all these cases, we can execute the task in the background and load the results into the UI. We can even update the UI as the task progresses.

Let’s see how.

2 simple ways to update the UI from the background

To start the process of removing code from your UI thread, you can either invoke Platform.runLater(), or use a JavaFX Task. Each solutions will work in different situations, so I’ll go through both below, as well as how to use them, and when to use them.

1. Platform.runLater()

If you want to keep the UI thread responsive, the key is outsourcing tasks to other threads. At that stage, you’ll find yourself working outside the Application thread. That means if you need to update the user interface as a result of the process, you’re on the wrong thread…

JavaFX has built in support for this, because it understands that a lot of long-running processes need to be outsourced. There are several ways to take advantage of JavaFX’s in-built concurrency, but the simplest way is to invoke Platform.runlater().

What does Platform.runLater() do?

This method accepts a Runnable object as a parameter. If you’ve never seen one, a Runnable is essentially just a wrapper for some executable code we want to run on a thread we can specify later. The Platform class will schedule this Runnable in a queue to execute “later”, and it will helpfully specify that it wants it to run on the Application thread.

Each Runnable is scheduled in an event queue. So, if you use runLater() multiple times, the Runnables you provide will be executed in the order they’re submitted. But, JavaFX will not guarantee when it will execute your code which sounds scary.

What does this event queue actually mean? JavaFX needs to balance its own tasks – the animation pulses, layout pulses and rendering – with the Runnables you provide it. It doesn’t guarantee when it will run your code because it can’t provide a cast-iron guarantee which frame your code will be executed in. But that doesn’t mean you’ll be waiting for long.

When to use Platform.runLater()

There are two basic rules for use:

  • You don’t need to return any variables from the Runnable
  • The Runnable you need to execute is short

The second one is particularly important because JavaFX is balancing your Runnable with its own workload. If you flood Platform.runLater() with intensive Runnables, the interface may become unresponsive.

How to use Platform.runLater()

Because of the simplicity of the code we’ll execute using Platform.runLater(), there are very simple use cases to go through:

Setting items in a TableView
ObservableList<Orders> databaseEntries = getDatabaseEntries();
Platform.runLater(()->{
    ordersTableView.setItems(databaseEntries);
});
Refocus the scene
if(!userInputValidated){
    Platform.runLater(()->{
        textField.requestFocus();
    });
}

2. Using JavaFX’s Task class

For more complicated chunks of code, it can be important to do one of two things:

  • Track the progress of the task
  • Interact with the UI either part-way, or at the end of the process
  • Return a value at the end of the process, which can also be used to update the UI.

What is a Task?

A Task object is a runnable object that provides additional functionality to:

  • Return a value from the asynchronous activity
  • Update the progress of the activity as it occurs.
  • Set additional executable code to be invoked on either successful completion of the task, or failure.

When to use a Task

Tasks should be used for longer, more complex code blocks that need to be carried out asynchronously.

Task progress is exposed as a property to enable property binding. This makes it perfect for use cases involving updating the user about the task’s progress while it’s running.

Use cases where you need to run some extra code on completion of the asynchronous code are also be supported. Conveniently, the functionality is backed by EventHandlers, meaning this extra code is executed on the Application thread.

Finally, Tasks also extend the Future class, meaning use cases involving asynchronous tasks that must return values are supported through the task.get() method.

How to use a Task

Let’s write a quick example to demonstrate all the basic features of the Task class.

We’ll create a Task that returns a list of Strings. In my head, it’s a list of book names from a database, but whatever it is, it’s a List. We specify this by parameterising the Task as we create it. It’s going to be a Task<List<String>>.

Inside the executable code, we’ll simulate loading some Strings from a database by sleeping the thread instead of using a live connection. We can set the executable code by overriding call(), which is executed when we start the task. Notice that call returns List<String>.

Creating the Task

We’re creating an anonymous class, so we’ll define an internal method getFromDatabase() which will simulate retrieving items from a database, but in reality we’ll simulate the delay between requesting and getting the data with Thread.sleep(1000).(While you’re here, check out this link for a comprehensive guide on how to connect JavaFX with a database!).

Task<List<String>> pollDatabaseTask = new Task<>() {
    List<String> bookNames = new ArrayList<>();

    @Override
    public List<String> call() {
        final int toLoad = 10;
        for (int i = 1; i <= toLoad; i++) {
            bookNames.add(getFromDatabase(i + 1));
            updateProgress(i, toLoad);
        }
        return bookNames;
    }

    private String getFromDatabase(int bookNumber) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "Book Number: " + bookNumber;
    }
};
Binding the Progress

We’ll be loading 10 items, so every time we load a String, we’ll update the task’s progress using the built-in method updateProgress(). We’ll take advantage of the task’s progress property by binding it to a ProgressBar, which we;ll load into our scene.

ProgressBar progressBar = new ProgressBar();
progressBar.progressProperty().bind(pollDatabaseTask.progressProperty());
rootNode.getChildren().add(progressBar);
Setting extra actions

Finally, when the task successfully completes, we’ll load all of our Strings into a ListView for our users to interact with. If we request the result before it’s ready, the Task will throw an exception, for example by updating the user that an error’s occurred, but we won’t define that action now.

pollDatabaseTask.setOnScheduled(event -> {
    try {
        listView.setItems(FXCollections.observableArrayList(pollDatabaseTask.get()));
    } catch (InterruptedException | ExecutionException e) {
        //We couldn't get the list, so we should change this to handle it gracefully. 
        e.printStackTrace();
    }
});
Running the Task

To execute our Task, we’ll create a Thread on which to run the task and starting the thread. Again, it’s up to you how you want to handle threads in your application, but in this case I’ll set the thread so it’s a ‘daemon’. That means it won’t keep running if my program shuts down.

Thread getItemsThread = new Thread(pollDatabaseTask);
getItemsThread.setDaemon(true);
getItemsThread.start();

This task should run for 10 seconds, with a progress bar that periodically increases in completeness. At the end of the process, your ListView should populate with the Strings we’ve generated.

Conclusions

It is possible to forcibly refresh a Scene by changing the width or height of the Scene by a fractional number of pixels (for example, 0.001). In fact, to refresh any node, you can change it’s width, or any node above it in the scene graph. This change will force the background windowing and rendering services to recalculate layout and rendering requirements for whichever elements have changed.

I cannot understate how inadvisable this is. In fact, in most cases, it won’t solve the problem you think you had.

The easiest way to check whether JavaFX is currently refreshing your scene is to add a pre-layout pulse listener to the Scene. If it is regularly refreshing, chances are you need to refresh an object in a way you weren’t expecting, such as refreshing your TableView.

If your scene isn’t refreshing, then forcing it to update will not solve your problem either. By changing the scene dimensions, you’ve ensured JavaFX knows your scene needs re-rendering. But, if you’re blocking the UI thread, it won’t have the chance to render the changes until you stop what you were doing.