我希望创建一个自定义TableColumn,它是一个CheckBox控件,表示是否选中了行。我们不想使用标准SHIFT或CONTROL + CLICK来处理此问题,因为用户往往会无意中点击并失去他们的选择。
我想做的是以下内容:
在这种情况下,列不应该是基础数据模型的一部分,而只是表示选择的内容。它不应该以任何方式依赖于数据模型。如果用户选中列标题中的复选框,则应选择表中的所有记录(不仅仅是可见的记录),如果取消选择,则应取消选择所有记录。
有没有办法以这种方式表示选择模型?
我遇到的第二个问题是,我的第一个尝试在SceneBuilder中不会显示为可选择的(因为我可以收集)TableColumn不是一个Node,因此似乎从导入中被忽略了?
非常感谢任何帮助。
由于
答案 0 :(得分:5)
一个可能的解决方案是扩展TableViewSkin
以添加带有复选框的TableColumn
,而此列不会使用用户的模型进行烘焙,但复选框选项的更改将影响表格的选择模型。
虽然只通过复选框选择工作正常,但您无法删除允许选择行的行为,因此您必须同时监听这两种情况。
此代码段有效,但尚未通过排序和修改模型进行测试,因此它只是自定义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。