创建框架类型列标题以在JavaFX 8中全选

pur*_*eon 3 tableview tablecolumn javafx-8

我正在寻找创建一个自定义TableColumn,这是一个CheckBox控件,表示是否选择了该行。我们不希望使用标准的SHIFT或CONTROL + CLICK来处理此问题,因为用户倾向于无意间单击并丢失选择。

我想做的是以下几点:

在此处输入图片说明

在这种情况下,该列不应成为基础数据模型的一部分,而应仅代表选择或不选择的内容。它不应该以任何方式依赖于数据模型。如果用户选中列标题中的复选框,则应选择表中的所有记录(而不仅是可​​见记录),如果取消选择,则应取消选择所有记录。

有没有办法以这种方式表示选择模型?

我遇到的第二个问题是,我的第一次尝试将不会在SceneBuilder中显示为可选择项,因为(从我的收集中可以看出)TableColumn不是Node,因此似乎在导入时被忽略了吗?

任何帮助将不胜感激。

谢谢

Jos*_*eda 5

可能的解决方案可能是扩展TableViewSkin以添加TableColumn带有复选框的,而此列并未随用户模型一起提供,但是复选框选择的更改将影响表的选择模型。

虽然仅通过复选框进行选择可以很好地工作,但是您无法删除允许选择行的行为,因此您必须侦听这两种情况。

该代码段有效,但是尚未经过对模型进行排序和修改的测试,因此,这只是custom的一个开始TableView

CheckTableView类

public class CheckTableView<T> extends TableView<T> {

    private ObservableList<T> selected;

    public CheckTableView() {
        this(FXCollections.observableArrayList());
    }

    public CheckTableView(ObservableList<T> items) {
        setItems(items);
        setEditable(true);
        getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
        skinProperty().addListener(new InvalidationListener() {
            @Override
            public void invalidated(Observable observable) {
                selected = ((CheckTableViewSkin) getSkin()).getSelectedRows();
                skinProperty().removeListener(this);
            }
        });
    }

    @Override
    protected Skin<?> createDefaultSkin() {
        return new CheckTableViewSkin<>(this);
    }

    public ObservableList<T> getSelectedRows() {
        return selected;
    } 
}
Run Code Online (Sandbox Code Playgroud)

CheckTableViewSkin类

public class CheckTableViewSkin<T> extends TableViewSkin<T> {

    private final TableColumn<T, Boolean> checkColumn;
    private final CheckBox headerCheckBox = new CheckBox();
    private final List<BooleanProperty> colSelected = new ArrayList<>();

    private final ChangeListener<Number> listener = (obs, ov, nv) -> {
        if (nv.intValue() != -1) {
            Platform.runLater(() -> {
                colSelected.get(nv.intValue()).set(!colSelected.get(nv.intValue()).get());
                refreshSelection();
            });
        }
    };
    private final ChangeListener<Boolean> headerListener = (obs, ov, nv) -> {
        Platform.runLater(() -> {
            IntStream.range(0, colSelected.size()).forEach(i -> colSelected.get(i).set(nv));
            refreshSelection();
        });
    };

    public CheckTableViewSkin(CheckTableView<T> control) {
        super(control);

        checkColumn = new TableColumn<>();

        headerCheckBox.selectedProperty().addListener(headerListener);
        checkColumn.setGraphic(headerCheckBox);

        // install listeners in checkboxes
        IntStream.range(0, control.getItems().size()).forEach(i -> {
            final SimpleBooleanProperty simple = new SimpleBooleanProperty();
            simple.addListener((obs, ov, nv) -> refreshSelection());
            colSelected.add(simple);
        });
        checkColumn.setCellFactory(CheckBoxTableCell.forTableColumn(colSelected::get));

        checkColumn.setPrefWidth(60);
        checkColumn.setEditable(true);
        checkColumn.setResizable(false);
        getColumns().add(0, checkColumn);

        getSelectionModel().selectedIndexProperty().addListener(listener);
    }

    private void refreshSelection() {
        // refresh list of selected rows
        getSelectionModel().selectedIndexProperty().removeListener(listener);
        getSelectionModel().clearSelection();
        AtomicInteger count = new AtomicInteger();
        IntStream.range(0, colSelected.size()).forEach(i -> {
            if (colSelected.get(i).get()) {
                getSelectionModel().select(i);
                count.getAndIncrement();
            }
        });
        headerCheckBox.selectedProperty().removeListener(headerListener);
        headerCheckBox.setSelected(count.get() == colSelected.size());
        headerCheckBox.selectedProperty().addListener(headerListener);
        // it may flick, but required to show all selected rows focused 
        getSkinnable().requestFocus();
        getSelectionModel().selectedIndexProperty().addListener(listener);
    }

    public ObservableList<T> getSelectedRows() {
        return getSelectionModel().getSelectedItems();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在是带有Application类的示例:

@Override
public void start(Stage primaryStage) {

    TableColumn<Person, String> firstNameColumn = new TableColumn<>("First Name");
    firstNameColumn.setCellValueFactory(p -> p.getValue().firstNameProperty());
    TableColumn<Person, String> lastNameColumn = new TableColumn<>("Last Name");
    lastNameColumn.setCellValueFactory(p -> p.getValue().lastNameProperty());

    CheckTableView<Person> tableView = new CheckTableView(FXCollections.observableArrayList(
            new Person("Hans", "Muster"), new Person("Ruth", "Mueller"), 
            new Person("Heinz", "Kurz"), new Person("Cornelia", "Meier"), 
            new Person("Anna", "Best"), new Person("Stefan", "Meier")
    ));

    tableView.getColumns().addAll(firstNameColumn, lastNameColumn);
    Scene scene = new Scene(tableView, 600, 400);
    primaryStage.setScene(scene);
    primaryStage.show();

}
Run Code Online (Sandbox Code Playgroud)

对于普通Person班:

public class Person {
    private final StringProperty firstName;
    private final StringProperty lastName;

    public Person(String firstName, String lastName) {
        this.firstName = new SimpleStringProperty(firstName);
        this.lastName = new SimpleStringProperty(lastName);
    }
    //getters & setters
}
Run Code Online (Sandbox Code Playgroud)

CheckTableView

此自定义控件也将与Scene Builder一起使用。