[英]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.