JavaFX中如何让TableView始终添加特定列进行排序?

问题描述 投票:0回答:1

我需要使表格始终按特定列排序(启用某些设置时)。例如,当用户按他想要的列对表格进行排序时,程序会自动添加另一列进行排序。

这是我的代码 - 程序必须始终按 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
列表中。

谁能告诉我如何解决吗?

java javafx
1个回答
0
投票

您所看到的行为似乎是设计好的。如果用户单击某列而不向下移动,则用户请求表重置排序顺序,以便它仅包含单击的列。因此,您单击“名称”列,希望将其从排序顺序中删除,但实际上您请求从排序顺序中删除所有其他列。然后,因为您的侦听器立即重新添加“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);
  }
}
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.