javamultithreadingjavafxsleepshow

JavaFX, code after primaryStage.show (), how?


my goal is to see the colorchange of the rectangle after every second ,after the Stage + Scene appears.

I researched and tried several things:

All in vain. In most situation the stage comes, then the program performs the colorchange in the background (without visualization) and at last the scene appears with the endresult. Or version 2: I see nothing, the code goes through and in the end comes immediately the final result.

Here is my code:

package sample;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;


public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        GridPane gridPane = new GridPane();

        Rectangle[] recs = new Rectangle[10];
        for (int i = 0; i < recs.length; i++) {
            recs[i] = new Rectangle(30, 30, Color.GREEN);
            recs[i].setStroke(Color.BLACK);
            gridPane.add(recs[i], i, 0);
        }

        primaryStage.setTitle("Code after primaryStage.show()");
        primaryStage.setScene(new Scene(gridPane, 400, 300));
        primaryStage.show();


        for (Rectangle rec : recs) {   
            Thread.sleep(1000);    
            rec.setFill(Color.ORANGE);
        }
    }


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

Solution

  • The issue here is that your loop is running on the main application thread, so it locks any GUI updates until it's completed.

    Perform the loop on its own thread instead and use Platform.runLater() to update each rectangle:

    import javafx.application.Application;
    import javafx.application.Platform;
    import javafx.scene.Scene;
    import javafx.scene.layout.GridPane;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Rectangle;
    import javafx.stage.Stage;
    
    
    public class Main extends Application {
    
        @Override
        public void start(Stage primaryStage) throws Exception {
            GridPane gridPane = new GridPane();
    
            Rectangle[] recs = new Rectangle[10];
            for (int i = 0; i < recs.length; i++) {
                recs[i] = new Rectangle(30, 30, Color.GREEN);
                recs[i].setStroke(Color.BLACK);
                gridPane.add(recs[i], i, 0);
            }
    
            primaryStage.setTitle("Code after primaryStage.show()");
            primaryStage.setScene(new Scene(gridPane, 400, 300));
            primaryStage.show();
    
            new Thread(() -> {
                for (Rectangle rec :
                        recs) {
                    try {
                        Thread.sleep(1000);
                        Platform.runLater(() -> rec.setFill(Color.ORANGE));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
    
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    So What's Happening?

    new Thread(() -> {
    

    Opens up a new thread in the background of your application so that the UI remains responsive.

    We can then start your loop within a try/catch block.

    Platform.runLater(() -> rec.setFill(Color.ORANGE));
    

    When working with a background thread, it's important to know that you cannot make changes to the UI directly. This line tells JavaFX to execute the rec.setFill() statement on the JavaFX Application thread.

    .start();
    

    You've already created the new Thread, this just starts it.