Lukas
Lukas

Reputation: 437

Show "Flash" messages with JavaFX

My goal is to have a function to show flash messages (small messages on the top, like notifications), which disappear after a couple of seconds. I currently achieve this with a stage and a javafx.concurrent.Task:

    public static void showFlashMessage(String message) {

        Stage window = new Stage();
        window.initStyle(StageStyle.UNDECORATED);

        ...

        Scene scene = new Scene(layout);
        window.setScene(scene);
        window.show();

        Task<Void> task = new Task<>() {
            @Override
            protected Void call() throws Exception {
                Thread.sleep(3000);
                return null;
            }
        };
        task.setOnSucceeded(e -> {
            window.close();
        });
        new Thread(task).start();
    }

But when the user clicks on the main stage, the flash message disappears. Calling window.toFront() only moves the window to the front ones. I want this message to look the same everywhere, like the build in Alert but furthermore I can't set window.initModality(Modality.APPLICATION_MODAL), because this wouldn't allow interaction with the main stage. Is there a good way to achieve this? I pretty much can't show these messages in the existing layouts, they have very different structures and it wouldn't look the same everywhere. And a second benefit of a stage is, that it survives layout changes in the background.

Upvotes: 1

Views: 466

Answers (2)

DaveB
DaveB

Reputation: 2143

As noted, setting Stage.setAlwaysOnTop(true) will work.

While Task works, for purely screen animations you should probably stick with the JavaFX Animation tools. Timeline works well here, and also allows you to make the flash message a little slicker by having a slight fade-in and fade out on the layout. Like this:

   private void showFlashMessage(String message) {
        Stage window = new Stage();
        window.initStyle(StageStyle.TRANSPARENT);
        VBox layout = new VBox(10, new Text(message));
        layout.setPadding(new Insets(3));
        layout.setBackground(new Background(new BackgroundFill(Color.PINK, new CornerRadii(3), Insets.EMPTY)));
        window.setScene(new Scene(layout, Color.TRANSPARENT));
        window.setAlwaysOnTop(true);
        Timeline timeline = new Timeline(new KeyFrame(Duration.ZERO, evt -> window.show(), new KeyValue(layout.opacityProperty(), 0)),
                                         new KeyFrame(Duration.millis(200), new KeyValue(layout.opacityProperty(), 1.0)),
                                         new KeyFrame(Duration.millis(2600), new KeyValue(layout.opacityProperty(), 1.0)),
                                         new KeyFrame(Duration.millis(3000), new KeyValue(layout.opacityProperty(), 0.2)));
        timeline.setOnFinished(evt -> window.close());
        timeline.play();
    }

Upvotes: 3

Basler182
Basler182

Reputation: 46

To make it work as you want it you can specify the owner Window for the stage. This has to happen before the stage is visible.

window.initOwner(ownerStage);

Otherwise you could also think about setting the Window to always on Top.

window.setAlwaysOnTop(true); 

You might also want to take a look into this post: https://stackoverflow.com/a/38373408/14464722

Which shows how to make a Android Style Toast.

Upvotes: 1

Related Questions