The following code makes the UI unresponsive for a short while after the button click, since too many Platform.runLater
calls are used. (Try scrolling the ListView
immediately after the button click.)
@Override
public void start(Stage primaryStage) {
ObservableList<Integer> data = FXCollections.observableArrayList();
ListView<Integer> listView = new ListView<>(data);
Button btn = new Button("Say 'Hello World'");
btn.setOnAction((ActionEvent event) -> {
new Thread(() -> {
for (int i = 0; i < 100000; i++) {
final int index = i;
Platform.runLater(() -> data.add(index));
}
}).start();
});
Scene scene = new Scene(new VBox(listView, btn));
primaryStage.setScene(scene);
primaryStage.show();
}
To prevent this instead of using a large number of updates, the following code uses a AnimationTimer
to run the update only once per frame:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.animation.AnimationTimer;
public class Updater {
@FunctionalInterface
public static interface UpdateTask {
public void update() throws Exception;
}
private final List<UpdateTask> updates = new ArrayList<>();
private final AnimationTimer timer = new AnimationTimer() {
@Override
public void handle(long now) {
synchronized (updates) {
for (UpdateTask r : updates) {
try {
r.update();
} catch (Exception ex) {
Logger.getLogger(Updater.class.getName()).log(Level.SEVERE, null, ex);
}
}
updates.clear();
stop();
}
}
};
public void addTask(UpdateTask... tasks) {
synchronized (updates) {
updates.addAll(Arrays.asList(tasks));
timer.start();
}
}
}
which allows grouping the updates using the Updater
class:
private final Updater updater = new Updater();
...
// Platform.runLater(() -> data.add(index));
updater.addTask(() -> data.add(index));