How to create a timer in JavaFX using scene builder?

3

Galera, I've been struggling for some months because of not getting a label to keep updating constantly.

I can do a timer, but when it comes to the interface created by scene builder , it hangs 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) {
            }      
}
    
asked by anonymous 10.01.2017 / 15:22

2 answers

0

As I see it, and from what I am studying, approach 1 is the most correct. However, it is important to highlight points here: The first and main is q to start a Task is necessary the class Servive, which inherits from Sevice, in case of approach 1, would be extends Service. In second place is q in a Task are the methods updateProgress (), updateMessage () and updateTitle () and some other methods can be overwritten, such as done (), canceled (), failed () and a few others. The 3rd and also, in my view, important, for the issue of good programming practice, is that the whole logic to update the time would be better put in a method, and in the call () method call that method, which would use the encapsulation practice

As I'm new here, I have not yet had time to properly see how to post a code, but I'll put it the way I know (I want to learn how to post a properly formatted code)

Here is the code for an example I made 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;
}

}

    
08.10.2017 / 08:06
0

I managed to do it in two different ways.

Approach 1:

Use a normal Task and update the label through a bind with the thread's 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 seems to be slower than expected, I do not know whether because of the communication between updateMessage and bind, but I believe it can be adjusted by changing the sleep time.

Approach 2:

Use Platform.runLater () to force the label to update using the 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 be a good programming practice

Additional Notices:

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

I hope I have helped!

    
27.06.2017 / 02:00