What is the significance of timer javafx?
I made a simple JavaFX app. It is just a Pomodoro timer, it works as expected.
The Pomodoro study pattern is a technique where you study for 25 minutes and take a 5-minute break and after 4 periods of this, you get a longer 15-minute break (and then repeat the whole thing). I made a simple countdown timer that keeps track of which study session you are on.
I am just wondering if I implemented the MVC pattern correctly. The Main class seems a bit redundant to me. Are there any other improvements I could make to the code?
Thanks
public class Controller {
public Controller(Model model, View view) {
view.getStartPauseBtn().setOnAction((e) -> {
if (model.isRunning()) {
model.pauseTimer();
model.setRunning(false);
} else {
model.startTimer();
model.setRunning(true);
}
});
}
}
import javafx.application.Platform;
import javafx.scene.control.Label;
import java.util.Timer;
import java.util.TimerTask;
public class Model {
private boolean isRunning = false;
private boolean isBreak = false;
private Timer timer = new Timer();
private int counter = 60 * 25; // 60 * 25 Testing:10
private final Label timerLbl, studySessionLbl, whatsNextLbl;
private int seconds, minutes, studySessionNum =0;
public Model(Label timerLbl, Label studySessionLbl, Label whatsNextLbl) {
this.timerLbl = timerLbl;
this.studySessionLbl = studySessionLbl;
this. whatsNextLbl = whatsNextLbl;
}
public boolean isRunning() {
return isRunning;
}
public void setRunning(boolean running) {
isRunning = running;
}
public void pauseTimer() {
timer.cancel();
}
public void startTimer() {
timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
Platform.runLater(() -> {
counter--;
seconds = counter `;
minutes = counter / 60;
if (seconds < 10> timerLbl.setText("0" + minutes + ":0" + seconds);
} else if (minutes < 10> timerLbl.setText("0" + minutes + ":" + seconds);
} else {
timerLbl.setText(minutes + ":" + seconds);
}
if (counter == 0 && studySessionNum == 4 && !isBreak) { // long break
isBreak = true;
counter = 15 * 60; // 15 * 60 Testing: 5
studySessionNum = 0;
} else if (counter == 0 && !isBreak) { // short break
counter = 5 * 60; // 5 * 60 Testing: 2
isBreak = true;
} else if (counter == 0) { // break finished
counter = 25 * 60; // 25 * 60 Testing: 10
studySessionNum++;
isBreak = false;
}
// update labels
studySessionLbl.setText("Study session number: " + studySessionNum);
if (isBreak) {
whatsNextLbl.setText("Next: Study session (25 minutes)");
} else if (studySessionNum == 4) {
whatsNextLbl.setText("Next: Long study break (15 minutes)");
} else {
whatsNextLbl.setText("Next: Short study break (5 minutes)");
}
});
}
}, 0, 1000);
}
}
Main class:
import javafx.application.Application;
import javafx.stage.Stage;
public class Pomodoro extends Application {
@Override
public void start(Stage stage) {
View view = new View();
Model model = new Model(view.getTimerLbl(), view.getStudySessionNumLbl(), view.getWhatsNextLbL());
new Controller(model, view);
stage.setTitle("Pomodoro Timer");
stage.setScene(view.getScene());
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I did not use a fxml file for the view.
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.scene.text.FontWeight;
public class View {
private final Label studySessionNumLbl, whatsNextLbL, timerLbl;
private final Button startPauseBtn;
private final Scene scene;
// UI Setup
public View() {
studySessionNumLbl = new Label();
whatsNextLbL = new Label();
HBox hbox = new HBox(studySessionNumLbl, whatsNextLbL);
hbox.setAlignment(Pos.CENTER);
hbox.setSpacing(10);
timerLbl = new Label("25:00");
timerLbl.setFont(Font.font("Verdana", FontWeight.BOLD, 70));
startPauseBtn = new Button("> ||");
VBox vbox = new VBox(timerLbl, startPauseBtn, hbox);
vbox.setAlignment(Pos.CENTER);
vbox.setSpacing(20);
scene = new Scene(vbox, 400, 400);
}
public Scene getScene() {
return scene;
}
public Label getStudySessionNumLbl() {
return studySessionNumLbl;
}
public Label getWhatsNextLbL() {
return whatsNextLbL;
}
public Button getStartPauseBtn() {
return startPauseBtn;
}
public Label getTimerLbl() {
return timerLbl;
}
}
It makes sense to execute this peace of code when the Scene is created. I will put it in the initilize() method inside the controller that has the timerLabel.
In your code you are creating a Timer, with a TimerTask that will be executed once per second (1000ms). This Timer javafx is executed in a different Thread so you need to use Platform.runLater to come back to the UI thread and perform the change in the interface (if not it will throw an error because you cannot modify interface elements outside the UI thread).
Finally, the variable secondsPassed has been declared as final because it is required to be used inside the TimerTask which has been declared as an anonymous inner class (check here for more information). As the array is final you cannot modify it. What you can do is modify its elements. In this case we only have one element (position 0), that you are using for counting the seconds.