按照某一列对JavaFX TableView进行排序

7
我正在尝试按照某一列来排序 TableView表。当我展示这个表格时,它确实被显示了出来,但是从这里和其他网站上所发现的内容来看,越看越让我感到困惑。

以下是TableView的代码:

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;
    }

我希望首先按性别对表进行排序。
据我所知,我必须创建一个SortedList / FilteredList,但我在网上找到的教程更加令我困惑。
我必须使用SortedList / FilteredList吗?还有更好的替代方案吗?
这是我找到的链接之一:http://code.makery.ch/blog/javafx-8-tableview-sorting-filtering/ 请问有人能为我简单解释一下吗?

1
据我所知,James_D(拥有JavaFX金徽章的53k用户)曾经对您提供的网站上的某个教程发表了负面评论。由于该教程似乎忽略了TableView已经提供了排序功能这一事实,我倾向于同意他的看法...这个网站可能不是最好的学习资源...(虽然我还没有在这个网站上阅读足够的内容来做一个有根据的总体质量评价) - fabian
1
这并不是唯一一个建议使用sortlists/FilteredLists来对TableView进行排序的网站。似乎这是我能找到的每个线程中建议的方法。 - Grimeire
真的让我很困惑,也没有什么帮助。另一方面,@fabian的答案解决了这个问题。非常感谢 ;) - Samarek
4个回答

24
没有必要自己创建一个已排序的列表。只需将要排序的列添加到您的TableViewsortOrder列表中即可:
table.getSortOrder().add(genColumn);

根据列中显示的类型,您可能还想设置比较器以使用自己(请参见 TableColumnBase.comparatorProperty )。

这种方法的好处是利用了用户交互提供的排序(单击列标题),而不是对支持列表进行排序(或使用列表的“排序视图”,这是 SortedList 所做的)。


1
很遗憾,这个解决方案对于值为字符串的列在我这里并没有起作用。 - Danny Harding
3
@DannyHarding,你记得调用TableView.sort()吗?如果不这样做,项目不会在初始时排序。 - davidrmcharles
@DavidCharles 是的,我在添加了 myTable.sort() 并稍微调整后成功让它工作了。我一直在交换表格中使用的列表,这增加了混乱。我不再交换列表,而是只交换列表中的项目。 - Danny Harding

13

我使用了被接受的答案让它工作,但我想分享一个完整的解决方案。

在这种情况下,在排序之前我提供表格数据。设置您想要排序的列的排序类型,将该列添加到排序顺序中,然后在任何您想要对表格进行排序的地方调用sort()方法。

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

0
避免使用varargs时出现“类型安全”警告,如果使用addColumns(elements..), 请进行列排序:
// 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));

补充说明,因为没有编译器提示关于: TableCell构造。 如果在列中定义了一个Object作为FactoryValue,例如显示图像和文本在单个列单元格中,则确保Observable Object类实现了Comparable或Comparator接口,并通过在可以为空的值上使用getValueSafe()的相应方法来比较对象。否则会发生异常,并且排序可能无法正常工作。
/* 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;
    });

实现:

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

现在可以安全地对该列进行排序!

    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;

        }
    }
}

如果您想在属性的任何值更改后自动触发排序,您必须在ObservableList的初始化中先将它们提取出来:

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

然后将其包装在 SortedList 中,它将响应并刷新 TableView 的排序顺序:

/** 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();
}

你的回答可以通过添加额外的支持信息来改进。请 [编辑] 添加更多细节,例如引用或文档,以便其他人可以确认您的答案是否正确。您可以在 帮助中心 中找到有关如何撰写良好答案的更多信息。 - Community

0

我只是想提一下,如果有人随后对其他列进行排序,则应将先前的清除以正常工作:

myTableView.getSortOrder().clear();

上面的代码来自Alper Güven先生:

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

并且滚动视图:

myTableView.scrollTo(myTableView.getSelectionModel().getSelectedItem());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接