docb45
docb45

Reputation: 304

Javafx controlsfx Notification doesn't display immediately when show() is called

I'm new to JavaFX and hung up on a minor issue when using ControlsFX Notification. During some longer running tasks (downloading some files for example) I'd like to show a notification when the downloads start, and again when they finish; however, when I try this the notifications both show up only after the task is finished.

Here's a sample with Thread.Sleep simulating a delay for some code to run:

 Notifications.create() .title("Title Text") .text("Hello World 1!") .darkStyle() .show();
 Thread.sleep(500);
 Notifications.create() .title("Title Text") .text("Hello World 2!") .darkStyle() .show();
 Thread.sleep(500);
 Notifications.create() .title("Title Text") .text("Hello World 3!") .darkStyle() .show();
 Thread.sleep(500);

When this runs, all the notifications show at once, not a half second apart.

Here's an image of what the notifications look like in the lower right corner

However, they fade away a half second apart..

So they're being created 500 milliseconds apart but not being displayed right away. How do I get them to show right away?

Upvotes: 2

Views: 1277

Answers (2)

fabian
fabian

Reputation: 82461

In your code you block the JavaFX application thread that is responsible for redrawing the UI. This way any changes in the GUI won't be visible and events won't be handled, ect.

You could use a Task and add a listener to the message property for diplaying the messages. Updates for this property are guarantied to be executed on the application thread. The only drawback is that for fast updates to the property some notifications could be skipped.

Task<Void> task = new Task<Void>() {

    @Override
    protected Void call() throws Exception {
        // update message property
        updateMessage("Hello World 1!");

        Thread.sleep(500);
        updateMessage("Hello World 2!");
        Thread.sleep(500);
        updateMessage("Hello World 3!");
        Thread.sleep(500);
        return null;
    }

};

// display message changes as notifications
task.messageProperty().addListener((observable, oldMessage, newMessage) ->
        Notifications.create().title("Title Text").text(newMessage).darkStyle().show());

// execute long running task on background thread
new Thread(task).start();

If you need every message to be displayed, you should use Platform.runLater to trigger the notifications.

Thread thread = new Thread(new Runnable() {

    private void postMessage(final String message) {
        Platform.runLater(() -> Notifications.create().title("Title Text").text(message).darkStyle().show());
    }

    @Override
    public void run() {
        try {
            postMessage("Hello World 1!");
            Thread.sleep(500);
            postMessage("Hello World 2!");
            Thread.sleep(500);
            postMessage("Hello World 3!");
            Thread.sleep(500);
        } catch (InterruptedException ex) {
            Logger.getLogger(NotificationsTestMain.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

});

thread.start();

Note that you should take care not to use Platform.runLater too often. In case there are many updates, it's sometimes best to group them. However in this case you'll probably not want to use notifications anyway, since there would simply be to many to display all of them. Nontheless here's some code that demonstrates grouping the updates:

private final List<String> messageQueue = new ArrayList<>();
private boolean updating;
private final Runnable updater = () -> {
    synchronized (messageQueue) {
        for (String message : messageQueue) {
            Notifications.create().title("Title Text").text(message).darkStyle().show();
        }
        messageQueue.clear();
        updating = false;
    }
};

private void postMessage(String message) {
    synchronized (messageQueue) {
        messageQueue.add(message);
        if (!updating) {
            updating = true;
            Platform.runLater(updater);
        }
    }
}

Upvotes: 3

mipa
mipa

Reputation: 10650

You don't show any context but I'd say your code is just blocking the JavaFX GUI thread. You have to execute your code above in a separate thread and wrap each notification creation in a Platform.runLater call. Alternatively you could also use an AnimationTimer or Timeline. Then you won't need a separate thread and no Thread.sleep.

Upvotes: 0

Related Questions