1

I'm trying to demonstrate to a few beginner programmers how to set a label on a JavaFX app to auto update. Basically they would like the value to decrease every minute or so on the label without any user interaction.

Java isn't my strong point and looking through some previous questions I get that I need to deal with threads and Runnable().

I have put the code together below that works, but I was just wondering if there is a better way of doing this or an easier way to demonstrate the same outcome with simpler code.

public class MainTimer2 extends Application {
    private int count = 100;
    private Label response = new Label(Integer.toString(count));

    public static void main(String[] args) {
        launch(args);
    }

    //Update function
    private void decrementCount() {
        count--;
        response.setText(Integer.toString(count));
    }

    @Override
    public void start(Stage myStage) {
        myStage.setTitle("Update Demo");

        //Vertical and horizontal gaps set to 10px
        FlowPane rootNode = new FlowPane(10, 10);

        rootNode.setAlignment(Pos.CENTER);

        Scene myScene = new Scene(rootNode, 200, 100);

        myStage.setScene(myScene);

        Thread thread = new Thread(new Runnable() {

            @Override
            public void run() {
                Runnable updater = new Runnable() {

                    @Override
                    public void run() {
                        decrementCount();
                    }
                };
                while (true) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                        System.out.println("Timer error");
                    }
                    // UI update is run on the Application thread
                    Platform.runLater(updater);
                }
            }
        });
        // don't let thread prevent JVM shutdown
        thread.setDaemon(true);
        thread.start();
        rootNode.getChildren().addAll(response);
        myStage.show();
    }
}
SamHoque
  • 2,978
  • 2
  • 13
  • 43
  • 4
    There are really many many ways to do this. The main methods to do this are using `TimeLine` and `Task`. You could also update the label via binding instead of explicitly calling `setText()`. In summary, I think your approach is already simple and suitable for beginners - doing other approaches is going to require you and your "audience" to be more familiar with JavaFX. However, do note that your approach doesn't use a lot of JavaFX API, other than the fact that the controls are from JavaFX. – Jai Oct 11 '18 at 08:12
  • Possible duplicate of [JavaFX periodic background task](https://stackoverflow.com/questions/9966136/javafx-periodic-background-task) – SedJ601 Oct 11 '18 at 13:47
  • The rule I try to follow is when you are going to be updating GUI elements use something from Animation Class. When not use Thread/Timer. – SedJ601 Oct 11 '18 at 13:49

1 Answers1

3

Count down by using PauseTransition:

import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.FlowPane;
import javafx.stage.Stage;
import javafx.util.Duration;

public class MainTimer2 extends Application {

    private int count = 100;
    private Label response = new Label(Integer.toString(count));

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage myStage) {
        myStage.setTitle("Update Demo");

        //Vertical and horizontal gaps set to 10px
        FlowPane rootNode = new FlowPane(10, 10);
        rootNode.setAlignment(Pos.CENTER);

        Scene myScene = new Scene(rootNode, 200, 100);
        myStage.setScene(myScene);

        rootNode.getChildren().addAll(response);
        myStage.show();
        update();
    }

    private void update() {

        PauseTransition pause = new PauseTransition(Duration.seconds(1));
        pause.setOnFinished(event ->{
            decrementCount();
            pause.play();
        });
        pause.play();
    }

    //Update function
    private void decrementCount() {
        count = (count > 0) ? count -1 : 100;
        response.setText(Integer.toString(count));
    }
}

Alternatively you could use Timeline:

    private void update() {

        KeyFrame keyFrame = new KeyFrame(
                Duration.seconds(1),
                event -> {
                    decrementCount();
                }
        );
        Timeline timeline = new Timeline();
        timeline.setCycleCount(Animation.INDEFINITE);
        //if you want to limit the number of cycles use 
        //timeline.setCycleCount(100);
        timeline.getKeyFrames().add(keyFrame);
        timeline.play();
    }
c0der
  • 18,467
  • 6
  • 33
  • 65
  • Actually anything that extends `Animation` will work similarly, only `Timeline` has that auto loop function built-in. – Jai Oct 11 '18 at 09:40