Maggie Mao
Maggie Mao

Reputation: 187

JavaFX updating node in scene not working

I'm trying to update a Label object, which is a child of a Pane in a scene. The label is supposed to display a count-down from a set amount of time, I used the bind function, and the StringProperty is updating, but the node itself is not updating.

Here's a sample. Might seem long but you can literally just copy and paste it into the IDE and it will show the problem.

import javafx.application.*;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.beans.property.*;
import java.util.Timer;
import java.util.TimerTask;

public class LabelUpdateExample extends Application{
    private StringProperty timeRemainingText = new SimpleStringProperty();
    public static void main(String[] args){
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Stage stage = new Stage();
        Pane canvas = new Pane();
        TimerField timerField = new TimerField(1);
        timeRemainingText = timerField.getTimeText();
        Label timeLabel = new Label("Start");
        timeLabel.textProperty().bind(timeRemainingText);

        // Adds the progress bar to the canvas, setting pref sizes
        canvas.setPrefSize(800, 600);
        canvas.getChildren().add(timeLabel);

        Scene scene = new Scene(canvas, 800, 600);
        stage.setScene(scene);
        stage.show();   
    }

    /**
     * Updates the text to show the new remaining time
     * 
     * @param timeText The text containing the new remaining time
     */
    public void setTimeText( StringProperty timeText ) {
        this.timeRemainingText = timeText;
        System.out.println("timeText: " + timeText.getValue());  // Debugging line
    }

    /**
     * A thingy that manipulates the timer and updates it in the toDoList
     */
    class TimerField extends Thread {
        private String timeText;

        // Variables related to time
        private int remTimeInSec;
        private int remMin;
        private int remSec;
        private Timer timer;

        /**
         * Default constructor of a TimerField object
         * 
         * @throws InterruptedException
         */
        public TimerField(int timeInMinutes) throws InterruptedException {
            // Sets up the variables related to time
            this.remMin = timeInMinutes;
            this.remSec = 0;
            this.remTimeInSec = timeInMinutes * 60;

            // Starts the timer that executes the time update task
            timer = new Timer();
            timer.scheduleAtFixedRate(new TimeUpdateTask(), 0, 1000); 
        }

        class TimeUpdateTask extends TimerTask{

            /**
             * Updates the time remaining, or stop everything when time is up
             */
            @Override
            public void run(){
                // TLDR: change the variables to represent one less second
                if(remSec > 0){ remSec--; }
                else{
                    remMin--;
                    remSec = 59;
                }
                remTimeInSec--;

                getTimeText();

                // Check for time up
                if( remTimeInSec <= 0 ){timer.cancel();}
            }
        }

        /**
         * Gives a StringProperty object describing time left
         */
        public StringProperty getTimeText( ){
            // Creates a new string indicating the remaining time
            this.timeText = "Time remaining: " + this.remMin +
            "mins " + this.remSec + "secs";
            StringProperty timeText = new SimpleStringProperty(this.timeText);

            // Update it on the TimedToDoList
            setTimeText(timeText);
            return timeText;
        }

        public boolean timeUp(){
            return this.remTimeInSec <= 0;
        }

    }
    // end of class TimerField
}

As the program runs, the debugging line does show that the StringProperty object is being updated, which makes me confused because I thought if I bound the Label's textProperty, the Label in the Scene would update with as the StringProperty updates. Any insight helps, thanks!

Upvotes: 0

Views: 612

Answers (1)

purring pigeon
purring pigeon

Reputation: 4209

The issue is that you are resetting your reference to timeRemainingText - so it is no longer bound. You need to change the value within it, not the object itself. Since you are using timer, you need to change the thread back to the FX thread - there are better ways to do that, but beyond the scope of this question.

Change the one method as noted below, and you will see this work

 public void setTimeText( StringProperty timeText ) {
        Platform.runLater(() -> 
        this.timeRemainingText.set(timeText.getValue()));
        System.out.println("timeText: " + timeText.getValue());  // Debugging line
    }

Upvotes: 4

Related Questions