更新时间:2023-11-17 14:10:46
一个可行的解决方案可能是扩展TableViewSkin
以添加带有复选框的TableColumn
,而此列未随用户模型一起提供,而是在复选框的选择将影响表的选择模型.
A possible solution could be extending TableViewSkin
to add a TableColumn
with the checkboxes, while this column is not baked with the user's model, but changes on the checkboxes selection will affect the table's selection model.
虽然仅通过复选框进行选择可以很好地工作,但是您不能删除允许选择行的行为,因此您必须侦听这两种情况.
While selecting only through the checkboxes works fine, you can't remove the behavior that allows selecting the rows as well, so you have to listen for both cases.
此代码段有效,但是尚未经过排序和修改模型的测试,因此,这只是自定义TableView
的开始.
This snippet works, but it hasn't been tested with sorting and modifying the model, so it will be just a start for a 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;
}
}
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();
}
}
现在使用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();
}
对于普通的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
}
此自定义控件也将与Scene Builder一起使用.
This custom control will work with Scene Builder as well.