2

When I work with the TableView<T> in JavaFX, I often call it's setAll() to give it a new List<T>. However, if the number of rows are somewhat large the scrollbar will go crazy as items are removed and added.

How do I stop this behavior? Is there a way I can "turn off" the visual updating while it is populating the new List<T>? It may feel unpolished and annoying to the user if it behaves like this.

TableView<MyType> tableView = ...;

tableView.getItems().setAll(someList); //goes crazy for a second

UPDATE I was asked to provide an SSCCE, and I came up with this. It does not reflect the problem as severely as my prod application. But if you move the scrollbar to the center and then click the "REBUILD" button it does jump around. Is there a way I can keep the scrollbar fixed during refresh? I imagine if I can do that, it will resolve my larger issue.

public final class TableViewJumpTest extends Application {

    private Random rand = new Random();

    private final Supplier<List<Integer>> listSupplier = () -> IntStream.range(0,rand.nextInt(100000))
            .mapToObj(i -> rand.nextInt(10000)).collect(Collectors.toList());

    @Override
    public void start(Stage stage) throws Exception {

        TableView<Integer> tableView = new TableView<>();

        TableColumn<Integer,Number> col = new TableColumn<>("VALUE");
        col.setCellValueFactory(cb -> new ReadOnlyIntegerWrapper(cb.getValue()));
        tableView.getColumns().add(col);

        tableView.getItems().setAll(listSupplier.get());


        VBox vBox = new VBox();

        vBox.getChildren().add(tableView);

        Button bttn = new Button("REBUILD");
        bttn.setOnAction(e -> tableView.getItems().setAll(listSupplier.get()));

        vBox.getChildren().add(bttn);

        stage.setScene(new Scene(vBox));

        stage.show();
    }

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

}
tmn
  • 11,121
  • 15
  • 56
  • 112
  • I've never seen this happen. Can you create a [MCVE]? – James_D Sep 10 '15 at 22:37
  • yeah sure, give me a moment... – tmn Sep 10 '15 at 22:38
  • Dang it, my SSCCE is behaving unlike my production application... – tmn Sep 10 '15 at 22:52
  • My theory is that the larger the list and the more column calculations there are, the more the behavior becomes apparent. – tmn Sep 10 '15 at 23:08
  • I think it's just scrolling to the same absolute index. Since the total number of items change, the scroll bar jumps. It doesn't seem to behave in any bad way other than that. What would you like the scrolling behavior to be when you replace all the items? You can easily just scroll to the top with `tableView.scrollTo(0)`. – James_D Sep 10 '15 at 23:09
  • I can confirm that previous comment. If you replace `mapToObj(i -> rnd.nextInt(10000))` with simply `boxed()` (so you get values in order), you'll see it maintains the same absolute index at the top of the visible portion, if it can. Since the total number of rows changes, that moves the scroll bar "thumb". This actually seems like fairly reasonable behavior to me; if you can specify how you would like it to behave differently, someone will likely be able to suggest an implementation. – James_D Sep 10 '15 at 23:27
  • Okay let me think about this and sleep on it. – tmn Sep 10 '15 at 23:28

1 Answers1

2

I've tried using the setAll() method and indeed it starts acting a bit weird when using very large collections. I would suggest replacing the getItems().setAll() with the following.

tableView.setItems(FXCollections.observableArrayList(yourList));

the setAll() method will iterate over both collections which I think makes the scrollbar jump, while setItems() just throws out the old Collection and replaces it with the new one. When using a very large dataset, the scrollbar will still shrink as the collection is added one by one into the observable collection, but not as eratically as with setAll().

If you want to keep the sorting, you probably want to get the sortColumn and sortType before you do the setItems as this will be removed when adding the new collection.

Patrick
  • 66
  • 4
  • Yes, this is much less erratic than `setItems()`. – tmn Sep 11 '15 at 09:49
  • That seems like a bug though, or at least worthy of requesting a tweak. Calling `setAll` on an observable list should really only need one or two notifications to observers of that list, so you really shouldn't observe the behavior described. Does `tableView.getItems().setAll(yourList.toArray(...));` work better? (If so, it might avoid having to manually reset the `sortColumn` and `sortType`.) – James_D Sep 11 '15 at 11:42
  • This setItems() does throw some errors wth record selections, and yeah I was thinking of filing a bug report for all these behaviors with setItems() and setAll(). I may di that tomorrow. – tmn Sep 12 '15 at 04:00