簡體   English   中英

如何僅按TreeTableView中的葉子排序?

[英]How do I sort by leaves only in a TreeTableView?

我使用JavaFX中的TreeTableView類來實現分組股票行情自動收錄器監視列表。 排序時,我只希望對庫存進行排序(而不是分組)。 當前,當我單擊“符號”列(例如)時,它將對每個組中的庫存進行排序(正如我期望的那樣),但同時也會對這些組進行排序。

就我而言,我只希望將每個組中的庫存排序,並在監視列表中保留這些組的順序。

我嘗試使用setSortMode()方法,但它僅支持以下模式:ALL_DESCENDANTS
ONLY_FIRST_LEVEL

我看了TreeItem類的源代碼,看起來當前不支持僅按葉子排序(但將來可能會這樣)。

  private void runSort(ObservableList<TreeItem<T>> children, Comparator<TreeItem<T>> comparator, TreeSortMode sortMode) 
  {
    if (sortMode == ALL_DESCENDANTS) {
        doSort(children, comparator);
    } else if (sortMode == ONLY_FIRST_LEVEL) {
        // if we are here we presume that the current node is the root node
        // (but we can test to see if getParent() returns null to be sure).
        // We also know that ONLY_FIRST_LEVEL only applies to the children
        // of the root, so we return straight after we sort these children.
        if (getParent() == null) {
            doSort(children, comparator);
        }
//  } else if (sortMode == ONLY_LEAVES) {
//      if (isLeaf()) {
//                // sort the parent once
//      }
//  } else if (sortMode == ALL_BUT_LEAVES) {
    } else {
        // Unknown sort mode
    }
  }

有什么方法可以解決此限制,而不必等待將來的JavaFX更新中添加支持?

回復很晚,但也許您仍然可以使用反饋。 我發現了兩種您想要的方法。

選項1:您可以設置自己的排序策略。 在這里,我設置了一個簡單的策略,僅對根目錄下的節點的子節點進行排序。 您必須為每個列以及每個列的組合實施一種排序方法。

package sample;

import javafx.application.Application;
import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeSortMode;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Callback;

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

public class SortOnlySome extends Application {

    private final TreeTableColumn<Person, String> tableColumn = new TreeTableColumn<>("Person");
    private final TreeTableColumn<Person, Integer> ageColumn = new TreeTableColumn<>("Age");

    @Override
    public void start(Stage primaryStage) {
        BorderPane borderPane = new BorderPane();

        TreeTableView<Person> treeTable = new TreeTableView();
        borderPane.setCenter(treeTable);

        setupColumns(treeTable);

        TreeItem<Person> root = new TreeItem<>(new Person("Root"));

        // Add some data
        populate(root);

        // Set the data into the treetable
        treeTable.setRoot(root);
        root.setExpanded(true);
        root.getChildren().forEach(c -> c.setExpanded(true));


        primaryStage.setScene(new Scene(borderPane));
        primaryStage.setTitle("Dont sort categories");
        primaryStage.show();
    }

    private void setupColumns(TreeTableView<Person> treeTable) {
        tableColumn.setCellValueFactory(param -> new ReadOnlyStringWrapper(param.getValue().getValue().displayName));
        ageColumn.setCellValueFactory(param -> new ReadOnlyIntegerWrapper(param.getValue().getValue().age).asObject());

        treeTable.getColumns().setAll(tableColumn, ageColumn);

        treeTable.setSortPolicy(new Callback<TreeTableView<Person>, Boolean>() {
            @Override
            public Boolean call(TreeTableView<Person> table) {
                TreeItem<Person> rootItem = table.getRoot();
                if (rootItem == null) return false;

                TreeSortMode sortMode = table.getSortMode();
                if (sortMode == null) return false;

                // Collecto the column comparators and merge them into 1
                List<Comparator<Person>> comparators = new ArrayList<Comparator<Person>>();
                table.getSortOrder().stream().forEachOrdered(ttc -> {
                    Comparator<Person> columnComparator = getComparatorForColumn(ttc);
                    comparators.add(columnComparator);
                });
                Comparator<Person> merged = mergeComparators(comparators);

                rootItem.getChildren().forEach(c -> {
                    c.getChildren().sort((o1, o2) -> merged.compare(o1.getValue(), o2.getValue()));
                    // TODO: Sort recursively down c.getChildren()
                });

                return true;
            }
        });
    }

    private Comparator<Person> getComparatorForColumn(TreeTableColumn<Person, ?> column) {
        int sign = column.getSortType() == TreeTableColumn.SortType.ASCENDING ? 1 : -1;
        if (column == tableColumn) {
            Comparator<Person> nameCompare = new Comparator<Person>() {
                @Override
                public int compare(Person o1, Person o2) {
                    Comparator c = column.getComparator();
                    Object obj1 = o1.displayName;
                    Object obj2 = o2.displayName;
                    return sign * c.compare(obj1, obj2);
                }
            };
            return nameCompare;
        } else if (column == ageColumn) {
            return new Comparator<Person>() {
                @Override
                public int compare(Person o1, Person o2) {
                    return sign * Integer.compare(o1.age, o2.age);
                }
            };
        } else {
            // TODO: Comparators for other columns
            return new Comparator<Person>() {
                @Override
                public int compare(Person o1, Person o2) {
                    return sign * 0;
                }
            };
        }
    }

    public static Comparator<Person> mergeComparators(final Collection<Comparator<Person>> multipleOptions) {
        return new Comparator<Person>() {
            public int compare(Person o1, Person o2) {
                for (Comparator option : multipleOptions) {
                    int result = option.compare(o1, o2);
                    if (result != 0) {
                        return result;
                    }
                }
                return 0;
            }
        };
    }

    private void populate(TreeItem<Person> root) {
        root.getChildren().add(new TreeItem<>(new Person("Group A")));
        root.getChildren().add(new TreeItem<>(new Person("Category 2")));
        root.getChildren().add(new TreeItem<>(new Person("Collection C")));

        // Add some people
        for (int i = 0; i < 17; i++) {
            String name = "Person " + (i + 1);
            Person someone = new Person(name);
            someone.age = 16 + i * 3;
            TreeItem categoryNode = root.getChildren().get(i % 3);
            categoryNode.getChildren().add(new TreeItem<>(someone));
        }
    }

    public class Person {
        public String displayName;
        public int age;

        public Person(String name) {
            this.displayName = name;
        }
    }

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

選項2:替代方法...讓我們看一下treetableview的功能。 為了比較元素,它創建了一個(新的)TableColumnComparatorBase。 在調用堆棧的某個時刻,類TableColumnComparatorBase將獲取列的值並將其用於比較:

@Override public int compare(S o1, S o2) {
    for (TableColumnBase<S,T> tc : columns) {
        if (! isSortable(tc)) continue;

        T value1 = tc.getCellData(o1);
        T value2 = tc.getCellData(o2);

        int result = doCompare(tc, value1, value2);

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

檢查如何比較它們的類TableColumnBase類:

public static final Comparator DEFAULT_COMPARATOR = (obj1, obj2) -> {
    if (obj1 == null && obj2 == null) return 0;
    if (obj1 == null) return -1;
    if (obj2 == null) return 1;

    if (obj1 instanceof Comparable && (obj1.getClass() == obj2.getClass() || obj1.getClass().isAssignableFrom(obj2.getClass()))) {
        return (obj1 instanceof String) ? Collator.getInstance().compare(obj1, obj2) : ((Comparable)obj1).compareTo(obj2);
    }

    return Collator.getInstance().compare(obj1.toString(), obj2.toString());
};

我找不到設置每列比較器的方法。 但是我們可以做的是確保單元格中的值實現Comparable並在不移動時返回0。 請注意,不是TreeItem的項目,而是CellValueFactory生成的項目應實現Comparable。 不幸的是,這還意味着我們需要添加自定義CellFactories或toStrings。 這一點都不漂亮,但是下面您將找到一個有效的示例:

import javafx.application.Application;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

import java.text.Collator;

public class SortOnlySome extends Application {

    @Override
    public void start(Stage primaryStage) {
        BorderPane borderPane = new BorderPane();

        TreeTableView<Person> treeTable = new TreeTableView();
        borderPane.setCenter(treeTable);

        setupColumns(treeTable);

        TreeItem<Person> root = new TreeItem<>(new Person("Root", false));

        // Add some data
        populate(root);

        // Set the data into the treetable
        treeTable.setRoot(root);
        root.setExpanded(true);
        root.getChildren().forEach(c -> c.setExpanded(true));


        primaryStage.setScene(new Scene(borderPane));
        primaryStage.setTitle("Dont sort categories");
        primaryStage.show();
    }

    private void setupColumns(TreeTableView<Person> treeTable) {
        TreeTableColumn<Person, NameSortWrapper> treeColumn = new TreeTableColumn<>("Person");
        treeColumn.setCellValueFactory(param -> new ReadOnlyObjectWrapper(new NameSortWrapper(param.getValue().getValue())));
        TreeTableColumn<Person, AgeSortWrapper> ageColumn = new TreeTableColumn<>("Age");
        ageColumn.setCellValueFactory(param -> new ReadOnlyObjectWrapper(new AgeSortWrapper(param.getValue().getValue())));
        treeTable.getColumns().setAll(treeColumn, ageColumn);
    }

    private void populate(TreeItem<Person> root) {
        root.getChildren().add(new TreeItem<>(new Person("Group A", false)));
        root.getChildren().add(new TreeItem<>(new Person("Category 2", false)));
        root.getChildren().add(new TreeItem<>(new Person("Collection C", false)));

        // Add some people
        for (int i = 0; i < 17; i++) {
            String name = "Person " + (i + 1);
            Person someone = new Person(name, true);
            someone.age = 16 + i * 3;
            TreeItem categoryNode = root.getChildren().get(i % 3);
            categoryNode.getChildren().add(new TreeItem<>(someone));
        }
    }

    // The class to show in the treetable
    // Problem 1: The data class defines whether sorting is allowed
    // Problem 2: Category nodes in the tree will also be TreeItem<Person>
    public class Person {
        public String displayName;
        public int age;
        boolean allowSorting = true;

        public Person(String name, boolean allowSort) {
            this.allowSorting = allowSort;
            this.displayName = name;
        }
    }

    // A class that implements Comparable and determines whether sorting should be done
    // To be extended for each column
    public abstract class SortWrapper implements Comparable {
        public final Person theRealData;

        public SortWrapper(Person theData) {
            this.theRealData = theData;
        }

        @Override
        public int compareTo(Object o) {
            if (theRealData.allowSorting) {
                return this.customCompare(o);
            } else {
                return 0;
            }
        }

        protected abstract int customCompare(Object o);

        @Override
        public String toString() {
            return super.toString();
        }
    }

    // A sort helper for the Person's name
    public class NameSortWrapper extends SortWrapper {
        public NameSortWrapper(Person theData) {
            super(theData);
        }

        @Override
        protected int customCompare(Object o) {
            return Collator.getInstance().compare(theRealData.displayName, ((NameSortWrapper) o).theRealData.displayName);
        }

        @Override
        public String toString() {
            if (theRealData == null) {
                return null;
            } else {
                return theRealData.displayName;
            }
        }
    }

    // A sort helper for the Person's age
    public class AgeSortWrapper extends SortWrapper {
        public AgeSortWrapper(Person theData) {
            super(theData);
        }

        @Override
        protected int customCompare(Object o) {
            return Integer.compare(theRealData.age, ((AgeSortWrapper) o).theRealData.age);
        }

        @Override
        public String toString() {
            // Note that null is returned when !allowSorting, this makes sure
            // that the rows do not show an age for the categories
            if (theRealData == null || !theRealData.allowSorting) {
                return null;
            } else {
                return Integer.toString(theRealData.age);
            }
        }
    }

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM