WiegleyJ
WiegleyJ

Reputation: 169

JAVAFX SortedList doesn't sort. (and making it breaks column sort.)

Using JavaFX (v15) I want a TableView that sorts by the entire row/object in the table if no columns are selected and by columns if a column header is clicked. If the columns are unclicked then it should return to displaying a sort of the rows by the objects natural order or a specified comparator.

So something like:

@FXML TableView<Person> tableview;
ObserverableList<Person> list = FXCollections.observableArrayList<>();
SortedList<Person> sorted = new SortedList<>(list);
sorted.comparatorProperty().bind(tableview.comparatorProperty());
tableview.setItems(sorted);

It does NOT appear sorted. It just gets whatever order the items iterate in the wrapped Observable List. Is this because SortedList constructed without a comparator is actually an unsorted list?? (Which is the dumbest design I've ever seen). It will sort by columns. Why doesn't the TableView when given a SortedList actually sort when columns aren't clicked?

If I change to:

@FXML TableView<Person> tableview;
ObserverableList<Person> list = FXCollections.observableArrayList<>();
SortedList<Person> sorted = new SortedList<>(list, (a,b) -> a.compareTo(b));
tableview.setItems(sorted);

Then the TableView will appear sorted... however, clicking on a column yields:

Feb 05, 2021 4:38:52 PM javafx.scene.control.TableView$3 call INFO: TableView items list is a SortedList, but the SortedList comparator should be bound to the TableView comparator for sorting to be enabled (e.g. sortedList.comparatorProperty().bind(tableView.comparatorProperty());).

If I put back the property binding between the SortedList and the TableView then of course my comparator is ignored.

How does one implement a table view that without columns selected for sort the rows will appear sorted by natural comparable order or by comparator and yet will sort by individual columns when those are selected.

Upvotes: 2

Views: 845

Answers (1)

kleopatra
kleopatra

Reputation: 51524

After playing a bit, it turned out that my initial comment

there's no direct simple (never investigated sortPolicy nor reacting to sortEvents :) support

was misleading (to put it mildly) - the not yet investigated sortPolicy looks like the way to go.

The basic idea is a custom sort policy that

  • checks if the items is-a SortedList
  • if so and the sortOrder is not empty, bind its comparator to the table's comparator and delegate to the default policy
  • if not, set a custom comparator (f.i. naturalOrder) and return

A utility method (beware: not formally tested!)

public static <T> Callback<TableView<T>, Boolean> emptySortOrderPolicy(Comparator<? super T> comparator) {
    return cc -> {
        if (cc.getItems() instanceof SortedList) {
            SortedList<T> sortedItems = (SortedList<T>) cc.getItems();
            if (cc.getSortOrder().isEmpty()) {
                sortedItems.comparatorProperty().unbind();
                sortedItems.comparatorProperty().set(comparator);
                return true;
            } else {
                sortedItems.comparatorProperty().bind(cc.comparatorProperty());
            }
        }
        return TableView.DEFAULT_SORT_POLICY.call(cc);
    };
}

Usage:

SortedList<ComparableItem> sorted = new SortedList<>(ComparableItem.items());
TableView<ComparableItem> table = new TableView<>(sorted);
table.setSortPolicy(emptySortOrderPolicy(Comparator.naturalOrder()));

Upvotes: 1

Related Questions