我需要在多个JavaFX TableView
中执行多路选择,以便在选择一个TableView
中的一行或多行时,也会选择/突出显示具有相关信息的所有其他TableView
(实际上,突出显示更合适,但选择是内置的,所以...)
我已经想出了一个有点笨拙的方法,但是它并不能很好地扩展到许多TableView
。
package multiwayselect;
import javafx.application.*;
import javafx.beans.property.*;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.control.TableView.TableViewSelectionModel;
import javafx.scene.layout.*;
import javafx.stage.*;
public class MultiwaySelectDemo extends Application {
private final ObservableList<Part> parts = FXCollections.observableArrayList();
private final ObservableList<Assembly> assemblies = FXCollections.observableArrayList();
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
buildModel();
stage.setTitle("multi-way selection demo");
final Region root = buildView();
stage.setScene(new Scene(root));
stage.show();
}
private void buildModel() {
Part cpu = new Part(1, "CPU Ryzen 5");
Part ram8 = new Part(2, "RAM 8GB DDR4 (1x8GB)");
Part ram16 = new Part(5, "RAM 16GB DDR4 (2x8GB)");
Part mobo1 = new Part(3, "MOBO ATX B550");
Part mobo2 = new Part(7, "MOBO ATX X570 RGB");
Part chassis = new Part(4, "CASE Standard ATX Case");
Part chassis1 = new Part(8, "CASE Gamer ATX Case w/RGB");
Assembly basicBox = new Assembly(1, "Basic AMD Box", cpu, ram8, mobo1, chassis);
Assembly gamerBox = new Assembly(2, "Gamer AMD Box", cpu, ram16, mobo2, chassis1);
assemblies.addAll(basicBox, gamerBox);
for (Assembly a : assemblies) {
for (Part p : a.parts) {
if (!parts.contains(p)) {
parts.add(p);
}
}
}
}
private boolean selecting = false;
private Region buildView() {
TableView<Part> partsTable = setupPartsTableView();
TableView<Assembly> assembliesTable = setupAssembliesTableView();
assembliesTable.getSelectionModel().getSelectedItems().addListener(new ListChangeListener<Assembly>() {
@Override
public void onChanged(Change<? extends Assembly> c) {
if (!selecting) {
selecting = true;
TableViewSelectionModel<Part> sm = partsTable.getSelectionModel();
sm.clearSelection();
for (Assembly a : c.getList()) {
for (Part p : a.partsProperty()) {
sm.select(p);
}
}
selecting = false;
}
}
});
partsTable.getSelectionModel().getSelectedItems().addListener(new ListChangeListener<Part>() {
@Override
public void onChanged(Change<? extends Part> c) {
if (!selecting) {
selecting = true;
TableViewSelectionModel<Assembly> sm = assembliesTable.getSelectionModel();
sm.clearSelection();
for (Part p : c.getList()) {
for (Assembly a : assemblies) {
if (a.partsProperty().contains(p)) {
sm.select(a);
}
}
}
selecting = false;
}
}
});
return new SplitPane(assembliesTable, partsTable);
}
private TableView setupAssembliesTableView() {
final TableView tableView = new TableView(assemblies);
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
final TableColumn<Assembly, Integer> idColumn = new TableColumn<>("id");
idColumn.setCellValueFactory(cell -> cell.getValue().idProperty());
final TableColumn<Assembly, String> nameColumn = new TableColumn<>("name");
nameColumn.setCellValueFactory(cell -> cell.getValue().nameProperty());
tableView.getColumns().addAll(idColumn, nameColumn);
return tableView;
}
private TableView setupPartsTableView() {
final TableView tableView = new TableView(parts);
tableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
final TableColumn<Part, Integer> idColumn = new TableColumn<>("id");
idColumn.setCellValueFactory(cell -> cell.getValue().idProperty());
final TableColumn<Part, String> nameColumn = new TableColumn<>("name");
nameColumn.setCellValueFactory(cell -> cell.getValue().nameProperty());
tableView.getColumns().addAll(idColumn, nameColumn);
return tableView;
}
public static class Part {
private ObjectProperty<Integer> id;
private StringProperty name;
public Part(int newId, String newName) {
this.id = new SimpleObjectProperty<>();
this.name = new SimpleStringProperty();
setId(newId);
setName(newName);
}
public final ObjectProperty<Integer> idProperty() {
return this.id;
}
public final void setId(int newValue) {
idProperty().set(newValue);
}
public final int getId() {
return idProperty().get();
}
public final StringProperty nameProperty() {
return this.name;
}
public final void setName(String newValue) {
nameProperty().set(newValue);
}
public final String getName() {
return nameProperty().get();
}
}
public static class Assembly {
private ObjectProperty<Integer> id;
private StringProperty name;
private ObservableList<Part> parts;
public Assembly(int newId, String newName, Part... newParts) {
id = new SimpleObjectProperty<>();
name = new SimpleStringProperty();
parts = FXCollections.observableArrayList();
setId(newId);
setName(newName);
parts.setAll(newParts);
}
public final ObjectProperty<Integer> idProperty() {
return id;
}
public final void setId(int newId) {
idProperty().set(newId);
}
public final int getId() {
return idProperty().get();
}
public final StringProperty nameProperty() {
return name;
}
public final void setName(String newValue) {
nameProperty().set(newValue);
}
public final String getName() {
return nameProperty().get();
}
public final ObservableList<Part> partsProperty() {
return parts;
}
}
}
基本上,selecting
提供了一个“处于选择模式”的互斥锁;如果没有它,两个听众就会吃掉堆栈。
以下哪种方法更好:(a)横向扩展到许多TableView
和/或(b)更符合JavaFX的习惯,或(c)更好?(注意:目前正在使用Java 8/JavaFX 8,但将采用更高版本的解决方案。)此外,还将听取关于更适合JavaFX习惯用法的数据表示方法的任何反馈。
1条答案
按热度按时间wgeznvg71#
我不认为有一种从根本上更简单的方法来做你已经在做的事情,你可以用一种更“可伸缩”的方式来重构它:
然后
这里需要注意的一点是,如果您使用相同的
SelectionManager
示例进行多个“多选”,它将使用相同的互斥锁。另一方面,不同的SelectionManager
示例将有自己的互斥锁。这(或它的一些变体)应该使您能够相当容易地以任何需要的方式在许多表上配置它。