0

I'm trying to remove all the nodes from my pane sequentially 1 by 1 so I can see each line being removed.To do this I have made a new thread and used the task class and wrapped the method delWalls() in a Platform.runLater() . I then used Thread.sleep thinking it would slow the loop slow so I could see the UI updating as each line is removed However what happens is the whole UI freezes up and then after the loop is done all the nodes have disappeared? Is there a way around this ... thanks

*all nodes are lines btw

 //loop calls delWalls() 1458 times to delete all 1458 nodes sequentailly
    Task task = new Task<Void>() {
        @Override
        public Void call() {
            Platform.runLater(() -> {
                try {
                    for (int i = 0; i <= 1458 - 1; i++) {
                        Thread.sleep(2);


                        delWalls();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });

            return null;
        }
    };
    new Thread(task).start();


    }

//delWalls method deletes one node each time it is called.

  public void delWalls() throws InterruptedException {

    pane.getChildren().remove(0);
 }
Fred
  • 750
  • 1
  • 7
  • 14
  • 4
    The `for-loop` and `Thread.sleep` are violating the single threaded nature of the framework - these operations are been executed within the context of the "main thread", where all the UI work is also done, so nothing is going to change until the loop is finished and the `runLater` callback exits – MadProgrammer Jul 04 '19 at 22:21
  • 2
    You're going to need to figure out a better update/animation method and since JavaFX is built for animation, there are a number possibilities available to you, maybe start with [Creating Transitions and Timeline Animation in JavaFX](https://docs.oracle.com/javafx/2/animations/jfxpub-animations.htm) or, because I come from a Swing background, something like [this](https://stackoverflow.com/questions/9966136/javafx-periodic-background-task), which uses "timer" style callback – MadProgrammer Jul 04 '19 at 22:25
  • yeah thanks for that boss got it working now with the timeline :) cheerssssss – Fred Jul 04 '19 at 22:47
  • Oops, sorry didnt see your comment before posting my answer. – Sai Dandem Jul 04 '19 at 22:51

1 Answers1

2

As @MadProgrammer said, you need to work with Timeline to get the desired effect. Below is a quick sample demo of how it can be done. Click "Add" to add nodes sequentially, and once all 10 nodes are added, click "remove" to remove them one by one.

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;

public class RemoveNodes_Demo extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        FlowPane pane = new FlowPane();
        pane.setVgap(10);
        pane.setHgap(10);

        Button button1 = new Button("Add Nodes");
        button1.setOnAction(e->{
            Timeline timeline = new Timeline(new KeyFrame(Duration.millis(400), x -> {
                StackPane sp = new StackPane();
                sp.setMinSize(100,100);
                sp.setStyle("-fx-background-color:black,red;-fx-background-insets:0,2;");
                pane.getChildren().add(sp);
            }));
            timeline.setCycleCount(10);
            timeline.play();
        });

        Button button2 = new Button("Remove Nodes");
        button2.setOnAction(e->{
            if(!pane.getChildren().isEmpty()){
                int count = pane.getChildren().size();
                Timeline timeline = new Timeline(new KeyFrame(Duration.millis(400), x -> {
                   if(!pane.getChildren().isEmpty()){ 
                      pane.getChildren().remove(0);
                   }
                }));
                timeline.setCycleCount(count);
                timeline.play();
            }
        });
        VBox root = new VBox(button1, button2,pane);
        root.setSpacing(10);
        Scene sc = new Scene(root, 600, 600);
        stage.setScene(sc);
        stage.show();
    }

    public static void main(String... a) {
        Application.launch(a);
    }
}
Sai Dandem
  • 8,229
  • 11
  • 26
  • Thanks alot , I was getting an out of bounds error but this solves that issue I see with the if(!pane.getChildren.isEmpty()){}. Thanks again – Fred Jul 04 '19 at 22:54
  • Yeah.. technically,I should also include an empty check just before removing the node (in case multiple timelines trying to remove). updated the code. – Sai Dandem Jul 04 '19 at 23:15