简体   繁体   English

JavaFX Tableview按自定义规则排序,然后按列选择

[英]JavaFX Tableview sort by custom rule, then by column selection

I am trying to sort a JavaFX TableView that contains 3 columns, one being a date, one being a name (string), and the last being the tags column (which is an enum). 我正在尝试对JavaFX TableView进行排序,其中包含3列,一列是日期,一列是名称(字符串),最后一列是标签列(这是一个枚举)。 What I want to do is make it so that no matter which column the table is currently sorting on, the rows will first sort by the tags (where if it has a certain tag, it is ordered above rows that do not have that certain tag). 我想做的是使它无论表当前在哪一列上排序,行都将首先按标签排序(如果它具有某个标签,则在没有该标签的行上方排序) )。

Thus when searching by name in ascending, the table will be order like so: 因此,当按名称升序搜索时,表的顺序将如下所示:

  • 'George' [tag] “乔治” [tag]
  • 'ZZ' [tag] 'ZZ'[标签]
  • 'Apple' [no-tag] “ Apple”(无标签)
  • 'Hello' [no-tag] “你好” [无标签]

etc. 等等

I've looked at column comparators, however I can only seem to specify for that column type, ie I would like to be able to specify the name-column comparator to take in the whole row's class object, and the name-column datatype (string), so I can then access the tags within that class instance - however this doesn't seem to be possible after looking around online. 我已经看过列比较器,但是我似乎只能为该列类型指定,即我希望能够指定名称列比较器来接受整行的类对象,以及名称列数据类型(字符串),这样我就可以在该类实例中访问标签了-但是在网上浏览后似乎无法实现。

Would it also be possible to persist this rule even when the tags column is selected to order desc (so it still places rows with the tag first). 即使选择了tag列以按desc排序时,也可以保留该规则(因此它仍将带有tag的行放在第一位)。 If not I can just disable sorting for the tag column 如果没有,我可以禁用标签列的排序

Thanks in advance for anyone that can point me in the right direction 预先感谢任何可以向我指出正确方向的人

As I mentioned in the comment, you can't just create a binding, which wraps the comparator of the TableView and gives it to SortedList. 正如我在评论中提到的那样,您不能仅创建绑定,该绑定将TableView的比较器包装并提供给SortedList。 Also SortedList is final so you can't extend it. 另外SortedList是final因此您不能扩展它。 Also using a custom TransformationList would not work, because the TableView has some hard coded stuff that starts with if (itemsList instanceof SortedList) . 也不能使用自定义的TransformationList ,因为TableView具有一些以if (itemsList instanceof SortedList)开头的硬编码内容。 This is what I ended up with (using java7 format and not labdas for the sake of clarity): 这就是我最终得到的结果(为了清楚起见,使用java7格式,而不是labdas):

    filteredList = new FilteredList<>(yourOriginalList);
    table.sortPolicyProperty().set(new Callback<TableView<YourObject>, Boolean>() {
        @Override
        public Boolean call(TableView<YourObject> param) {
            final Comparator<YourObject> tableComparator = transactionTable.getComparator();
            // if the column is set to unsorted, tableComparator can be null
            Comparator<YourObject> comparator = tableComparator == null ? null : new Comparator<YourObject>() {
                @Override
                public int compare(YourObject o1, YourObject o2) {
                    // first sort by your tag
                    final int tagCompare = o1.getTag().compareTo(o2.getTag());
                    if (tagCompare == 0) {
                        // secondly sort by the comparator that was set for the table
                        return tableComparator.compare(o1, o2);
                    }
                    return tagCompare;
                }
            };
            table.setItems(filteredList.sorted(comparator));
            return true;
        }
    });

    // you may also need to call
    table.setItems(filteredList.sorted(transactionTable.getComparator()));

I agree with @Miklos solution. 我同意@Miklos解决方案。 Below is a complete working example of your requirement (based on @Miklos solution) to sort first by Tag column before doing sort on the selected column. 以下是您的要求(基于@Miklos解决方案)的完整工作示例,该要求在对选定列进行排序之前先按标签列进行排序。 [This is not a full solution, but this should give you some direction to start with and make it working completly as per your needs] [这不是一个完整的解决方案,但这应该为您提供一些指导,使其完全按照您的需要运行]

import javafx.application.Application;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

import static javafx.scene.control.TableColumn.SortType.ASCENDING;

public class CustomComparatorTableColumn extends Application {
    @Override
    public void start(Stage primaryStage) throws Exception {
        ObservableList<Device> devices = FXCollections.observableArrayList();
        devices.add(new Device(1, "Apple", "Zebra", TagType.TAG));
        devices.add(new Device(2, "BlackBerry", "Parrot", TagType.TAG));
        devices.add(new Device(3, "Amazon", "Yak", TagType.NO_TAG));
        devices.add(new Device(4, "Oppo", "Penguin", TagType.NO_TAG));

        TableView<Device> tableView = new TableView<>();
        TableColumn<Device, String> nameCol = new TableColumn<>("Name");
        nameCol.setCellValueFactory(param -> param.getValue().nameProperty());

        TableColumn<Device, String> displayCol = new TableColumn<>("Display");
        displayCol.setCellValueFactory(param -> param.getValue().displayProperty());

        TableColumn<Device, TagType> tagCol = new TableColumn<>("Tag");
        tagCol.setCellValueFactory(param -> param.getValue().tagTypeProperty());

        tableView.getColumns().addAll(nameCol, displayCol, tagCol);
        tableView.setItems(devices);

        tableView.setSortPolicy(tv -> {
            final ObservableList<Device> itemsList = tableView.getItems();
            if (itemsList == null || itemsList.isEmpty()) {
                return true;
            }
            final List<TableColumn<Device, ?>> sortOrder = new ArrayList<>(tableView.getSortOrder());
            if (!sortOrder.isEmpty()) {
                // If there is no Tag column in the sort order, always adding as the first sort to consider.
                if (!sortOrder.stream().anyMatch(tc -> tc.getText().equals("Tag"))) {
                    sortOrder.add(0, tagCol);
                }
                FXCollections.sort(itemsList, new TableColumnComparator<>(sortOrder));
            }
            return true;
        });

        Scene sc = new Scene(tableView);
        primaryStage.setScene(sc);
        primaryStage.show();

    }

    class TableColumnComparator<S> implements Comparator<S> {
        private final List<TableColumn<S, ?>> allColumns;

        public TableColumnComparator(final List<TableColumn<S, ?>> allColumns) {
            this.allColumns = allColumns;
        }

        @Override
        public int compare(final S o1, final S o2) {
            for (final TableColumn<S, ?> tc : allColumns) {
                if (!isSortable(tc)) {
                    continue;
                }
                final Object value1 = tc.getCellData(o1);
                final Object value2 = tc.getCellData(o2);

                @SuppressWarnings("unchecked") final Comparator<Object> c = (Comparator<Object>) tc.getComparator();
                final int result = ASCENDING.equals(tc.getSortType()) ? c.compare(value1, value2)
                        : c.compare(value2, value1);

                if (result != 0) {
                    return result;
                }
            }
            return 0;
        }

        private boolean isSortable(final TableColumn<S, ?> tc) {
            return tc.getSortType() != null && tc.isSortable();
        }
    }

    class Device {
        IntegerProperty id = new SimpleIntegerProperty();
        StringProperty name = new SimpleStringProperty();
        StringProperty display = new SimpleStringProperty();
        ObjectProperty<TagType> tagType = new SimpleObjectProperty<>();

        public Device(int id, String n, String d, TagType tag) {
            this.id.set(id);
            name.set(n);
            tagType.set(tag);
            display.set(d);
        }

        public String getName() {
            return name.get();
        }

        public StringProperty nameProperty() {
            return name;
        }

        public void setName(String name) {
            this.name.set(name);
        }

        public ObjectProperty<TagType> tagTypeProperty() {
            return tagType;
        }

        public StringProperty displayProperty() {
            return display;
        }

    }

    enum TagType {
        TAG, NO_TAG;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM