Reputation: 437
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
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
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