I’m currently working on a program in which users can create their on time intervals for different exercises. Once start is pressed, the countdown begins for the first exercise. Once it is done, a sound is played and countdown begins for the second one and so on until all the exercises are done and removed. I use a timer which after every 1 second, subtracts the time of the exercise by 1. The problem is, I can’t seem to find a way to restart Timers in java. When all exercises are done I can stop the timer but I can’t seem to find a way to restart it for when I want to create new exercises and go through the process again. I can’t also find a way to pause and play the timer again during a particular process. I’m new to JavaFX, so I would really appreciate if you could guide me how I can change my code to achieve what I’m looking for.
Timer timer = new Timer(); startButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { //Timer task=new TimerTask(); timer.schedule(new TimerTask() { @Override public void run() { running=true; if (running==true) { if (workoutsList.size() == 0) { return; } if (workoutsList.size() == 1 && workoutsList.get(0).time == 1) { text.setText("over!"); mediaPlayer1.play(); workoutsList.clear(); workouts.getItems().clear(); timer.cancel(); return; } workoutsList.get(0).time -= 1; if (workoutsList.get(0).time == 0) { workoutsList.remove(0); mediaPlayer.play(); return; } workouts.getItems().clear(); workouts.refresh(); for (int i = 0; i < workoutsList.size(); i++) { workouts.getItems().add(workoutsList.get(i)); } } } }, 0, 1000); } }); stopButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { timer.cancel(); running=false; } });
Advertisement
Answer
Since the Timer
does nothing except track time it would be better to use the javafx.animation
API. This gives you certain advantages:
- Everything happens on the JavaFX Application Thread, meaning no concurrency issues.
- You can make use of the
currentTime
andcycleDuration
properties ofAnimation
to track the time left in the countdown. - You can make use of the
play()
,pause()
, andstop()
methods ofAnimation
to control the timer. - You can use the
onFinished
property ofAnimation
to play you sound when the timer completes.
Here’s an example using PauseTransition
, though you could also use e.g. Timeline
.
import javafx.animation.Animation; import javafx.animation.PauseTransition; import javafx.application.Application; import javafx.beans.binding.Bindings; import javafx.beans.binding.StringBinding; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.stage.Stage; import javafx.util.Duration; public class Main extends Application { @Override public void start(Stage primaryStage) { // javafx.util.Duration PauseTransition timer = new PauseTransition(Duration.minutes(5)); // timer.setOnFinished(e -> /* play sound */); Button startBtn = new Button("Start"); startBtn.setOnAction(e -> timer.play()); Button pauseBtn = new Button("Pause"); pauseBtn.setOnAction(e -> timer.pause()); Button resetBtn = new Button("Reset"); resetBtn.setOnAction(e -> timer.stop()); Label label = new Label(); label.setFont(Font.font("Monospaced", 20)); label.textProperty().bind(timeLeftAsString(timer)); HBox hbox = new HBox(10, startBtn, pauseBtn, resetBtn); hbox.setAlignment(Pos.CENTER); VBox root = new VBox(25, label, hbox); root.setPadding(new Insets(25)); root.setAlignment(Pos.CENTER); primaryStage.setScene(new Scene(root)); primaryStage.show(); } private StringBinding timeLeftAsString(Animation animation) { return Bindings.createStringBinding( () -> { double currentTime = animation.getCurrentTime().toMillis(); double totalTime = animation.getCycleDuration().toMillis(); long remainingTime = Math.round(totalTime - currentTime); // java.time.Duration java.time.Duration dur = java.time.Duration.ofMillis(remainingTime); return String.format( "%02d:%02d:%03d", dur.toMinutes(), dur.toSecondsPart(), dur.toMillisPart()); }, animation.currentTimeProperty(), animation.cycleDurationProperty()); } }
Side note: You mention a sound is played when the timer completes, and I can see a call to mediaPlayer.play()
. Considering the nature of the program I assume the sound being played is relatively short. If that’s the case you should consider using AudioClip
instead of MediaPlayer
.