How to create a stopwatch in Javafx using Scene Builder?

Asked

Viewed 851 times

4

Guys, I’ve been a few months with some stranded projects, because I can’t get a label keep up to date.

I can make a stopwatch, but when it comes to the interface created by Scene Builder, it crashes because I could not update the label, I think.

Please help me, I’m in great need.

Example:

package progrma.de.teste.nivel.pkg2;

import java.net.URL;
import java.util.ResourceBundle;
import java.util.Timer;
import java.util.TimerTask;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;

public class interface2 implements Initializable {

    @FXML
    private Label label;
    private int contador=0;
    @FXML
    private void handleButtonAction(ActionEvent event) {

           Timer tm = new Timer();
            tm.scheduleAtFixedRate(new TimerTask(){
            @Override
            public void run() {
                contador++;
                int seg  =  contador %60;
                int min  =  contador /60;
                int hora =  min      /60;
                min     %=  60;
                label.setText(String.format("%02d:%02d:%02d:",hora,min,seg));
            }
        },1000,1000);
    }
    @Override
    public void initialize(URL url, ResourceBundle rb) {
            }      
}

3 answers

0

Here I do so...

    @FXML
    public void initialize() {

        //Data atual do Sistema
        LocalDate hoje = LocalDate.now();
        DateTimeFormatter formataDia = DateTimeFormatter.ofPattern("dd/MM/yyyy");
        String diaAtual = hoje.format(formataDia);

        lblData.setText(diaAtual);

        Timeline relogio = new Timeline(new KeyFrame(Duration.ZERO, e -> {

        //Data atual do Sistema
        LocalDateTime agora = LocalDateTime.now();
        DateTimeFormatter formataHora = DateTimeFormatter.ofPattern("HH:mm:ss");
        String horaAtual = agora.format(formataHora);

        lblHora.setText(horaAtual);

        }),
                new KeyFrame(Duration.seconds(1))
        );
        relogio.setCycleCount(Animation.INDEFINITE);
        relogio.play();
}

0

As far as I’m concerned, and as far as I’m concerned, approach one is the right one. However, it is important to highlight points here: The 1st and the main one is q to start a Task it takes the class Servive, which inherits from Sevice, in the case of approach 1, would be extends Service. In 2nd place is q in a Task and q are the methods updateProgress(), updateMessage() and updateTitle() and some other methods q can be overwritten, like done(), cancelled(), () and some others. The 3rd and also, in my opinion, important, by the question of good programming practice, is that the whole logic to update the time would be better if put in a method, and, in the call() method call this method, that would use the practice of encapsulation

As I’m new here I haven’t had time to see the proper way to post a code, but I’ll put it the way I know ( I intend to learn as well as post a properly formatted code )

Follow the code of an example I did for the same exercise:

public class UpdateService extends Service<String> {

private int segundo;
private int minuto;
private int hora;
private int contador;

private String path = getClass().getResource("/som_de_submarino.mp3").toString();

private Media media = new Media(path);

private MediaPlayer mediaPlayer = new MediaPlayer(media);

@Override
protected Task<String> createTask() {
    return new Task<String>() {

        @Override
        protected String call() throws Exception {

            while (true) {
                cronometrar();
                Thread.sleep(1000);
            }


        }

        private void cronometrar() throws Exception {

            contador++;
            segundo = contador % 60;
            minuto = contador / 60;
            hora = minuto / 60;
            minuto %= 60;

            for (int i = 0; i < 6; i++) {
                if (minuto == (i * 10) && segundo == 0) {
                    mediaPlayer.play();
                }
            }

            if (hora > 0) {
                updateMessage(String.format("%02d:%02d:%02d", hora, minuto, segundo));
            } else {
                updateMessage(String.format("%02d:%02d", minuto, segundo));
            }
        }

    };

}

public void setContador(int contador) {
    this.contador = contador;
}

}

  • In the text where I spoke of the Service class it did not appear q it inherits from Service and the type of return. Ex: extends Service<void>

  • It is another way to solve but here the main thing is to give an orientation. We use the Service class when there are changes in the UI code, which can only be made by FX thread and Task when the operations are in the background. Using Task favors isolating FX Thread for rendering for this reason I only used Task and Bind to solve this problem.

  • In addition, you can start a Task without inheritance by creating a thread and associating the task to it, thus: new Thread(task). start();

  • In the way you did the exercise, seeing the code and the way a Task was created, it was assumed that the code was done via programming. In the example q passed the exercise was done via FXML file. Therefore, I am assuming the fact q a Controller class was created. To complete my solution just instantiate an object of the Updadeservice class. ex: Updateservice service = new Updateservice() in the Controller class and in the initialize() method of the Controller class create a Bind type: label.textProperty(). bind(service.messageProperty());

  • In the way you did the exercise, seeing the code and the way a Task was created, it is concluded that the code was done via programming. In the example q passed the exercise was done via FXML file. Therefore, I am assuming the fact q a Controller class was created. To complete my solution, simply instantiate an object of the Updadeservice class. ex: Updateservice service = new Updateservice() in the Controller class and in the initialize() method of the Controller class create a one-dimensional Bind type: label.textProperty(). bind(service.messageProperty());

  • Also I would like to mention q, in the example q passed, of the Service class, the timer is set to wake up every 10 minutes. However, the way it is not working, you need to put another if in the time method after the 1º for type: if (Seconds == 30) { mediaPlayer.stop(); }. This way the timer will wake up every 10 minutes, or as each one wants to set up

Show 1 more comment

0

I managed to do it two different ways.

Approach 1:

Use a normal Task and update the label via a bind with the thread messageProperty():

Task<Void> task = new Task<Void>() {
    @Override
    protected Void call() throws Exception {
        while(!isCancelled()){
            contador++;
            int seg  =  contador %60;
            int min  =  contador /60;
            int hora =  min      /60;
            min     %=  60;
            
            updateMessage(String.format("%02d:%02d:%02d",hora,min,seg));
            Thread.sleep(1000);
        }
        
        return null;
        }
    };
label.textProperty().bind(task.messageProperty());

Problems:

Sometimes looks like be slower than expected, I don’t know if due to the communication between updateMessage and bind, but I believe can be adjusted by changing the Sleep time.

Approach 2:

Using Platform.runLater() to force the label update using Application Thread:

Timer tm = new Timer();
    TimerTask task = new TimerTask(){
        @Override
        public void run(){
            Platform.runLater(new Runnable() {
            @Override
            public void run() {
                contador++;
                int seg  =  contador %60;
                int min  =  contador /60;
                int hora =  min      /60;
                min     %=  60;
                label.setText(String.format("%02d:%02d:%02d",hora,min,seg));
            }});
        }
    };
tm.scheduleAtFixedRate(task,1000,1000);

Problems:

2 run() nested does not seem to me to be a good practice of programming

Additional warnings:

  • Both solutions must be stopped manually as threads continue to run after application shutdown!
  • It is up to the reader to use the solution seems more accurate, since the performance measurements are a personal opinion, I did not make a comparative test of the two.

I hope I’ve helped!

Browser other questions tagged

You are not signed in. Login or sign up in order to post.