我需要使表格始终按特定列排序(启用某些设置时)。例如,当用户按他想要的列对表格进行排序时,程序会自动添加另一列进行排序。
这是我的代码 - 程序必须始终按 ID ASC 排序:
public class JavaFxTest7 extends Application {
private static record Student(int id, String name, int mark) {};
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) {
var table = this.createTable();
table.getSortOrder().addListener((ListChangeListener<? super TableColumn<Student, ?>>)(change) -> {
//if some settings enabled then
var idColumn = table.getColumns().get(0);
if (!table.getSortOrder().contains(idColumn)) {
idColumn.setSortType(TableColumn.SortType.ASCENDING);
table.getSortOrder().add(0, idColumn);
}
});
var scene = new Scene(table, 300, 200);
stage.setScene(scene);
stage.show();
}
private TableView<Student> createTable() {
var table = new TableView<Student>();
table.getItems().addAll(new Student(1, "Billy", 3),
new Student(2, "Johnny", 4), new Student(3, "Mickey", 5));
var idColumn = new TableColumn<Student, Integer>("ID");
idColumn.setCellValueFactory((data) -> new ReadOnlyObjectWrapper<>(data.getValue().id()));
var nameColumn = new TableColumn<Student, String>("Name");
nameColumn.setCellValueFactory((data) -> new ReadOnlyStringWrapper(data.getValue().name()));
var markColumn = new TableColumn<Student, Integer>("Mark");
markColumn.setCellValueFactory((data) -> new ReadOnlyObjectWrapper<>(data.getValue().mark()));
table.getColumns().addAll(idColumn, nameColumn, markColumn);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_FLEX_LAST_COLUMN);
return table;
}
}
这就是结果:
按 ID 排序始终按计划添加。问题是,使用我的解决方案,不可能删除按其他列排序 - 无论您单击
name
列多少次,name
列始终存在于 sortOrder
列表中。
谁能告诉我如何解决吗?
您所看到的行为似乎是设计好的。如果用户单击某列而不向下移动,则用户请求表重置排序顺序,以便它仅包含单击的列。因此,您单击“名称”列,希望将其从排序顺序中删除,但实际上您请求从排序顺序中删除所有其他列。然后,因为您的侦听器立即重新添加“ID”列,所以看起来好像出了问题。
也就是说,如果您考虑到侦听器中的上述行为,那么实现您想要的似乎是可能的(至少在 JavaFX 22 中)。这是一个例子。但请注意,由于(必要)使用
runLater
,通过 UI 更改排序顺序时,列标题的内容有时会闪烁。
package com.example;
import java.util.ArrayList;
import java.util.List;
import javafx.application.Platform;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.SortType;
import javafx.scene.control.TableView;
public class PrefixedSortOrder<S> {
/**
* Makes it so the {@link TableView#getSortOrder() sortOrder} of {@code table} always contains the
* columns of {@code prefixCols}, in order, as a prefix. This constraint is maintained whether the
* {@code sortOrder} is changed programmatically or via user interaction.
*
* @param <S> The item type of the {@code table}.
* @param table the {@code TableView}
* @param prefixCols the columns to always have at the start of the {@code sortOrder}
*/
public static <S> void install(TableView<S> table, List<TableColumn<S, ?>> prefixCols) {
var pso = new PrefixedSortOrder<>(table, prefixCols);
table.getSortOrder().addListener(pso::onChanged);
}
private final ObservableList<TableColumn<S, ?>> sortOrder;
private final List<TableColumn<S, ?>> prevSortOrder;
private final List<TableColumn<S, ?>> prefixCols;
private boolean fixingSortOrder;
private PrefixedSortOrder(TableView<S> table, List<TableColumn<S, ?>> prefixCols) {
this.sortOrder = table.getSortOrder();
this.prevSortOrder = new ArrayList<>(sortOrder);
this.prefixCols = List.copyOf(prefixCols);
}
private void onChanged(ListChangeListener.Change<? extends TableColumn<S, ?>> c) {
if (fixingSortOrder) return;
if (!sortOrder.containsAll(prefixCols)) {
// Sort order changed in manner consistent with a simple click.
fixingSortOrder = true;
// Avoid modifying the list from within the listener.
Platform.runLater(
() -> {
fixSortOrder();
cacheSortOrder();
fixingSortOrder = false;
});
} else {
// Sort order changed in manner consistent with a shift+click.
cacheSortOrder();
}
}
private void fixSortOrder() {
if (sortOrder.size() == 1) {
// Sort order changed via user interaction (probably).
var column = sortOrder.get(0);
if (prefixCols.contains(column)) {
// User toggled sort direction of prefix column.
sortOrder.setAll(prevSortOrder);
} else if (wasSortOrderCleared(column)) {
// User removed non-prefix column from sort order.
sortOrder.setAll(prefixCols);
} else {
// User toggled sort direction of non-prefix column.
sortOrder.addAll(0, prefixCols);
}
} else {
// Sort order changed programmatically (probably).
var temp = new ArrayList<>(sortOrder);
temp.removeAll(prefixCols);
temp.addAll(0, prefixCols);
sortOrder.setAll(temp);
}
}
private boolean wasSortOrderCleared(TableColumn<S, ?> column) {
return column.getSortType() == SortType.ASCENDING && prevSortOrder.contains(column);
}
private void cacheSortOrder() {
prevSortOrder.clear();
prevSortOrder.addAll(sortOrder);
}
}