调用 show() 时,Javafx controlsfx 通知不会立即显示

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

我是 JavaFX 的新手,在使用 ControlsFX 通知时遇到了一个小问题。在一些较长的 运行ning 任务中(例如下载一些文件),我想在下载开始时显示一个通知,并在下载完成时再次显示;然而,当我尝试这个时,通知都只在任务完成后显示。

这是一个示例,其中 Thread.Sleep 模拟了一些代码到 运行:

的延迟
 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);

当这 运行 秒时,所有通知一次显示,而不是相隔半秒。

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

However, they fade away a half second apart..

所以它们是相隔 500 毫秒创建的,但不会立即显示。如何让它们立即显示?

您没有显示任何上下文,但我会说您的代码只是阻塞了 JavaFX GUI 线程。您必须在单独的线程中执行上面的代码,并将每个通知创建包装在 Platform.runLater 调用中。 或者,您也可以使用 AnimationTimer 或时间轴。那么您将不需要单独的线程,也不需要 Thread.sleep.

在您的代码中,您阻塞了负责重绘 UI 的 JavaFX 应用程序线程。这样,GUI 中的任何更改都将不可见,事件也不会被处理,等等。

您可以使用 Task 并将侦听器添加到 message 属性 以显示消息。此 属性 的更新保证在应用程序线程上执行。唯一的缺点是为了快速更新 属性 可以跳过一些通知。

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();

如果您需要显示每条消息,您应该使用 Platform.runLater 来触发通知。

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();

注意不要经常使用Platform.runLater。如果有很多更新,有时最好将它们分组。但是,在这种情况下,您可能无论如何都不想使用通知,因为要显示所有这些通知会很简单。不过这里有一些代码演示了对更新进行分组:

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);
        }
    }
}