There are a couple things wrong with your current approach.
You must never sleep, block, or perform long-running tasks on the JavaFX Application Thread. Otherwise, you will cause the application to become unresponsive because the thread cannot process user-generated events or schedule render "pulses".
The scene will not be redrawn until the JavaFX Application Thread has finished its current work. That means setting a label's text property to different values via consecutive statements will not work as you expect. Only the last update to the text property will be rendered.
You basically want a periodic task executed on the JavaFX Application Thread. The way to implement that is via an animation. See my answer here for more information.
In your case, your task is essentially a countdown timer. A relatively straightforward way to implement a countdown timer in JavaFX is to use a PauseTranstion
. Set the transition's duration to the desired value, then make use of the currentTime
property to calculate the remaining time of the animation. You can also use the onFinished
property to perform an action once the transition completes.
Here's an example:
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.util.Duration;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
Button button = new Button("Create account...");
button.setOnAction(e -> {
e.consume();
showAccountCreatedAlert(primaryStage);
});
primaryStage.setScene(new Scene(new StackPane(button), 600, 400));
primaryStage.show();
}
private void showAccountCreatedAlert(Window owner) {
Alert alert = new Alert(AlertType.INFORMATION);
alert.initOwner(owner);
alert.setHeaderText("Account Created");
// the countdown timer
PauseTransition timer = new PauseTransition(Duration.seconds(5));
alert.contentTextProperty()
.bind(createRemainingSecondsMessage("Dialog closing in %d seconds.", timer));
timer.setOnFinished(e -> alert.close()); // close the alert when the timer completes
// Start the timer when the alert is shown. Stop it if the alert is
// closed before the timer completes.
alert.setOnShown(e -> timer.play());
alert.setOnHidden(e -> timer.stop());
alert.showAndWait();
}
private StringBinding createRemainingSecondsMessage(String format, PauseTransition timer) {
return Bindings.createStringBinding(() -> {
Duration timeRemaining = timer.getDuration().subtract(timer.getCurrentTime());
int secondsRemaining = (int) timeRemaining.toSeconds() + 1;
return String.format(format, secondsRemaining);
}, timer.currentTimeProperty(), timer.durationProperty());
}
public static void main(String[] args) {
Application.launch(Main.class, args);
}
}