7

I'm trying to sort a TableView table by a certain column. When I display the table, it's shown but from what I'm finding on here and other sites it's just confusing me more and more.

here is the tableview code

public TableView<Animal> animalLostLocation(ArrayList<Animal> list)
    {

        TableColumn<Animal, String> atypeColumn = new TableColumn<>("Animal Type");
        atypeColumn.setMinWidth(200);
        atypeColumn.setSortable(false);
        atypeColumn.setCellValueFactory(new PropertyValueFactory<>("aType"));

        TableColumn<Animal, String> descriptionColumn = new TableColumn<>("Description");
        descriptionColumn.setMinWidth(200);
        descriptionColumn.setSortable(false);
        descriptionColumn.setCellValueFactory(new PropertyValueFactory<>("description"));

        TableColumn<Animal, String> breedColumn = new TableColumn<>("Breed");
        breedColumn.setMinWidth(80);
        breedColumn.setSortable(false);
        breedColumn.setCellValueFactory(new PropertyValueFactory<>("breed"));

        TableColumn<Animal, String> nameColumn = new TableColumn<>("Name");
        nameColumn.setMinWidth(200);
        nameColumn.setSortable(false);
        nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));

        TableColumn<Animal, Catergory> catColumn = new TableColumn<>("Category");
        catColumn.setMinWidth(200);
        breedColumn.setSortable(false);
        catColumn.setCellValueFactory(new PropertyValueFactory<>("category"));

        TableColumn<Animal, Integer > ageColumn = new TableColumn<>("Age");
        ageColumn.setMinWidth(50);
        ageColumn.setSortable(false);
        ageColumn.setCellValueFactory(new PropertyValueFactory<>("age"));

        TableColumn<Animal, Integer > idColumn = new TableColumn<>("ID");
        idColumn.setMinWidth(50);
        idColumn.setCellValueFactory(new PropertyValueFactory<>("ID"));

        TableColumn<Animal, Boolean > genColumn = new TableColumn<>("Male");
        genColumn.setMinWidth(50);
        genColumn.setSortable(false);
        genColumn.setCellValueFactory(new PropertyValueFactory<>("gender"));

        table = new TableView<Animal>();

        table.setItems(getAnimal(list));

        table.getColumns().addAll(genColumn, idColumn, nameColumn, atypeColumn, ageColumn, breedColumn, descriptionColumn, catColumn);
        return table;
    }

I want to sort the table by gender initially.

As far as I know I have to create a SortedList/FilteredList but the tutorial I found online is just confusing me even more.

Do I have to use SortedList/FilteredList or are there better alternatives?

Here's one of the links I found http://code.makery.ch/blog/javafx-8-tableview-sorting-filtering/

Can anyone dumb it down for me please?

fabian
  • 80,457
  • 12
  • 86
  • 114
Grimeire
  • 339
  • 1
  • 6
  • 19
  • 1
    AFAIK James_D (= 53k user with javafx gold badge) made a negative comment about one of the tutorials on the site you linked once. Since this tutorial seems to ignore the fact that `TableView` already provides a sorting feature, I'm inclined to agree him... This site may not be the best learning source... (I haven't read enough on this site to do a well-founded review of the overall quality though) – fabian Mar 26 '16 at 21:10
  • 1
    thats not the only site thats suggests to sort tableview using sortlists/FilteredLists. its seems to be the method suggested on every thread i could find. – Grimeire Mar 27 '16 at 14:00
  • Seriously confused me as well and didn't help much. @fabian's answer, on the other hand, solved the problem. Herzlichen Dank ;) – Samarek May 15 '18 at 06:57

4 Answers4

24

There is no need to create a sorted list yourself. Just add the column to sort by to the sortOrder list of your TableView:

table.getSortOrder().add(genColumn);

Depending on the types displayed in the column(s), you may also want to set the comparator to use yourself (see TableColumnBase.comparatorProperty)

This approach has the benefit of using the sorting provided by user interaction (clicking on the column headers) instead of sorting the backing list (or using a "sorted view" of a list, which is what SortedList does).

fabian
  • 80,457
  • 12
  • 86
  • 114
  • 1
    Unfortunately this solution did not work for me on a column whose values are strings – Danny Harding Dec 10 '18 at 22:45
  • 3
    @DannyHarding, did you remember to call `TableView.sort()`? The items won't be *initially* sorted unless you do that. – davidrmcharles Dec 14 '18 at 14:12
  • @DavidCharles yes I was able to get it to work after adding `myTable.sort()` and fiddling with it a little. I was swapping out the list being used in the table, so that was adding to the confusion. Instead of swapping out the list I just swapped out the items that were in the list – Danny Harding Dec 15 '18 at 15:04
13

I used the accepted answer to make it work but I want to share a complete solution.

In this case, I provided data to table before sorting. Set sort type for column that you want to sort, add that column to sort order, then call sort() method where ever you want to sort table.

myColumn.setSortType(TableColumn.SortType.DESCENDING);
myTableView.getSortOrder().add(myColumn);
myTableView.sort();
Alper Güven
  • 329
  • 4
  • 15
0

Avoid the varargs "Type safety" warning, if used addColumns(elements..), with column sorting:

// warning when
tableView.addColumns(elements..)

// avoid with: use of add() 
tableView.getColumns().add(columIndex);
tableView.getColumns().add(columTitle);

// or with: use of Arrays.asList() 
tableView.getSortOrder().setAll(Arrays.asList(columnTitle));

Additive note, because there is no compiler hint about: TableCell constructions. If instead of a primitive type, an Object is defined in the column as FactoryValue, for example to display an image and text in a single column cell, then make sure that the Observable Object Class implements Comparable or Comparator interface and compare the object's with the corresponding method by use of getValueSafe() on values that can be null. Otherwise an exception occurs and sorting may not work correctly.

/* modified column cell layout for column title */
columnTitle.setCellValueFactory(c -> new SimpleObjectProperty<AbstractNodeItem>(c.getValue()));

    columnTitle.setCellFactory(col -> {
        TableCell<AbstractNodeItem, AbstractNodeItem> cell = new TableCell<>();

        cell.itemProperty().addListener((observableValue, oldLink, newLink) -> {
            if (newLink != null) {
                Node graphic = newLink.getIconImageView();
                cell.graphicProperty().bind(Bindings.when(cell.emptyProperty()).then((Node) null).otherwise(graphic));
                cell.textProperty().bind(Bindings.when(cell.emptyProperty()).then((String) null).otherwise(newLink.propertyTitle()));

                if (!cell.getStyleClass().contains("cell-title")) {
                    cell.getStyleClass().add("cell-title");
                }
            }
        });
        return cell;
    });

Implementation:

    class AbstractNodeItem implements Comparable<AbstractNodeItem>{
//propertys...
        public int compareTo(AbstractNodeItem o) {
            return this.propertyTitle.getValueSafe().compareTo(o.propertyTitle.getValueSafe());
        }

Now it is safe to sort the column!

    private void sortTableItems() {
        switch (SortOrder.get()) {

            case ID :
                columnIndex.setSortType(TableColumn.SortType.ASCENDING);
// This statement is enough to trigger a new sort when using a SortedList.
                tableView.getSortOrder().setAll(Arrays.asList(columnIndex));
                break;
            case TITLE :
                columnTitle.setSortType(TableColumn.SortType.ASCENDING);
                tableView.getSortOrder().setAll(Arrays.asList(columnTitle));
                break;

        }
    }
}

If you want to trigger the sorting automatically after any value change of propertys, you have to extract them beforehand in the initialization of the ObservableList:

this.list = FXCollections.observableArrayList(item -> new Observable[]{
                item.propertyIndex, item.propertyTitle
        });

Then wrapped in SortedList it will respond and refresh the TableView sort order:

/** does not select or refocus **/
private void showContentInTable(ObservableList<? extends AbstractNodeItem> list, boolean sortTableUpdate) {
    /* check down arrow key down, do not refresh content until these arrow keys are released */
    if (null == list || list.isEmpty() || pressedKeyArrowMultiple) {
        if (!tableView.getItems().equals(this.listEmpty)) {
            tableView.setItems(this.listEmpty);
        }
        tableView.focusTraversableProperty().set(false);
        return;
    }

    this.sortedListTableData.comparatorProperty().unbind();
    sortedListTableData = null;
    sortedListTableData = new SortedList<>(list);
    sortedListTableData.comparatorProperty().bind(tableView.comparatorProperty());

    sortedListTableData.addListener((ListChangeListener.Change<? extends AbstractNodeItem> c) -> {
        boolean somethingRemovedOrAdded = false;
        while (c.next()) {
            if (c.wasRemoved()) {
                somethingRemovedOrAdded = true;
            } else if (c.wasAdded()) {
                somethingRemovedOrAdded = true;
            }
            if (somethingRemovedOrAdded) {
                break;
            }
        }

        if (somethingRemovedOrAdded) {
            tableView.setFocusTraversable(!sortedListTableData.isEmpty());
        }
    });

    tableView.setItems(null);
    tableView.setItems(sortedListTableData);
    // important: when list isn't empty, make table focus traversable again 
    tableView.focusTraversableProperty().set(!list.isEmpty());

    if (sortTableUpdate) {
        sortTableItems();
    }

    // don't use CPU intensive statement
    // this.tableView.refresh();
}
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Sep 13 '22 at 02:59
0

I just wanted to mention if someone is sorting other column afterwards, previous should be cleared to work correctly:

myTableView.getSortOrder().clear();

Code above from Mr.Alper Güven:

myColumn.setSortType(TableColumn.SortType.DESCENDING);
myTableView.getSortOrder().add(myColumn);
myTableView.sort();

And to scroll view:

myTableView.scrollTo(myTableView.getSelectionModel().getSelectedItem());
Seco
  • 21
  • 5