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
.