How to force Java FX scene refresh?

34,886

This is an old question and it caught my eye since this is a very general issue faced by people new to JavaFX.

The problem that OP is facing is because he updates all the rectangles at once, without waiting.

OP can wait by either creating a new Thread, put the thread on sleep for an estimated seconds for every iteration of the loop and then update the color of the rectangle on JavaFX application thread by using Platform.runLater.

@Override
public void handle(Event arg0) {
  new Thread(() -> {
     for(int i=0; i<10; i++) {
       try {
          Thread.sleep(1000); // Wait for 1 sec before updating the color
       } catch (InterruptedException e) {
          e.printStackTrace();
       }
       int finalI = i;
       Platform.runLater(() -> rectangles[finalI].setFill(Color.RED));// Update on JavaFX Application Thread
   }
}).start();

The above snippet is more of a traditional way of doing things. If we want to use the "JavaFX" ways of doing things, we can achieve the same by using an Animation.

Below is a code snippet which will wait for x-seconds before changing the color of the rectangle. It doesn't need any extra thread since the wait is handled by PauseTransition applied for each rectangle.

startButton.setOnMouseClicked(new EventHandler<Event>() {
    @Override
    public void handle(Event arg0) {
        for(int i=0; i<10; i++) {
            PauseTransition pauseTransition = new PauseTransition(Duration.seconds(i));
            int finalI = i;
            pauseTransition.setOnFinished(event -> rectangles[finalI].setFill(Color.RED));
            pauseTransition.play();
        }
    }
});

It creates a PauseTransition for each rectangle and depending on its index in the array rectangles, it waits for the same number of seconds before updating the color.

Share:
34,886
Nikopol
Author by

Nikopol

"Genius is one percent inspiration, ninety-nine percent perspiration." "All we have to decide is what to do with the time that is given us."

Updated on July 09, 2022

Comments

  • Nikopol
    Nikopol almost 2 years

    I have an Java FX scene with a start button and several rectangles which represent the tiles of a map. I also have drawn a sphere which represents my explorer (it has to explore the map), but I am having difficulties with running the animation.

    In my OnMouseClicked handler for the start button, I start an algorithm for exploring the map which changes the position of the sphere and the colors of the tiles which have been visited. The problem is that the scene won't update itself while the algorithm is running, so I only get to see how the final scene will look like (after the algorithm has stopped running). How can I force a scene update so I can see all the color changes sequentially?

    Later edit:

    import javafx.application.Application;
    import javafx.event.Event;
    import javafx.event.EventHandler;
    import javafx.event.EventType;
    import javafx.geometry.Insets;
    import javafx.scene.Group;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.image.Image;
    import javafx.scene.layout.HBox;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Rectangle;
    import javafx.stage.Stage;
    
    public class Test extends Application {
    
    private static final double boxOuterSize = 50;
    private static final double boxInnerSize = 48;
    private static final double boxCornerRadius = 20;
    
    private Stage applicationStage;
    private Scene applicationScene;
    
    private static double   sceneWidth  = 1024;
    private static double   sceneHeight = 800;
    private static HBox     container = new HBox();
    private static Group    root = new Group();
    private Rectangle[] rectangles = new Rectangle[10];
    
    @Override
    public void start(Stage mainStage) throws Exception {
    
        applicationStage = mainStage;
        container.setSpacing(10);
        container.setPadding(new Insets(10, 10, 10, 10));
    
        try {
            applicationScene = new Scene(container, sceneWidth, sceneHeight);
            applicationScene.addEventHandler(EventType.ROOT,(EventHandler<? super Event>)this);
            applicationScene.setFill(Color.WHITE);
    
        } catch (Exception exception) {
            System.out.println ("exception : "+exception.getMessage());
        }
    
        applicationStage.setTitle("HurtLockerRobot - Tema 3 IA");
        applicationStage.getIcons().add(new Image("icon.png"));
        applicationStage.setScene(applicationScene);
    
        for(int i=0; i<10; i++) {
            Rectangle r = new Rectangle();
            r.setFill(Color.BLUE);
            r.setX(i * boxOuterSize);
            r.setY(0);
            r.setWidth(boxInnerSize);
            r.setHeight(boxInnerSize);
            r.setArcHeight(boxCornerRadius);
            r.setArcWidth(boxCornerRadius);
            r.setSmooth(true);
            rectangles[i] = r;
            root.getChildren().add(rectangles[i]);
        }
    
        container.getChildren().add(root);
        Button startButton = new Button("Start");
        startButton.setOnMouseClicked(new EventHandler<Event>() {
            @Override
            public void handle(Event arg0) {
                for(int i=0; i<10; i++) {
                    rectangles[i].setFill(Color.RED);
                    // TODO: some kind of scene refresh here
                }
            }
        });
        container.getChildren().add(startButton);
    
        applicationStage.show();
    }
    
    public static void main(String[] args) {
        launch(args);
    }
    }
    

    Initially all the rectangles are blue. The behavior I want to obtain here is to see the rectangles changing colors sequentially. The problem is that I only get to see the end result (all the rectangles change their color at the same time).

  • Nikopol
    Nikopol over 11 years
    That exception got in there because I forgot to remove the line with the addEventHandler. Anyway, that exception doesn't influence the application or the problem I'm facing in any way (it gets thrown after new Scene..). Thread.sleep() is not a solution because it puts the GUI JavaFX thread to sleep so that will surely not work (how can the GUI be refreshed if the GUI thread is sleeping?).
  • joey rohan
    joey rohan over 11 years
    I mean take a pause, say 100 milisec and then color another rectangle
  • Nikopol
    Nikopol over 11 years
    The thread running the event handler is the Java FX thread. This thread also does the rendering of the scene. So if I pause this thread , that also means that the scene won't be refreshed.
  • Nikopol
    Nikopol over 11 years
    The example you provided doesn't help me with my problem. As I have mentioned before, the behavior I want to obtain here is to see the rectangles changing colors sequentially. Also, if I were to use an AnimationTimer I would still have the problem with all the animations running at the same time.
  • joey rohan
    joey rohan over 11 years
    @Nikopol No,you won't have any problem if you use animation timer.Try and post an sscce if you have any problems