java 是否有更惯用的方法来使用TableView执行交互、多路选择?

czq61nw1  于 2023-02-02  发布在  Java
关注(0)|答案(1)|浏览(98)

我需要在多个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习惯用法的数据表示方法的任何反馈。

wgeznvg7

wgeznvg71#

我不认为有一种从根本上更简单的方法来做你已经在做的事情,你可以用一种更“可伸缩”的方式来重构它:

public class SelectionManager {
    private boolean selecting ;
    private <S,T> void setUpMultiSelection(TableView<S> firstTable, TableView<T> secondTable, BiPredicate<S,T> shouldSelect) {
        firstTable.getSelectionModel().getSelectedItems().addListener((Change<? extends S> c) -> {
            if (selecting) return;
            selecting = true ;
            secondTable.getSelectionModel().clearSelection();
            for (T t : secondTable.getItems()) {
                for (S s : firstTable.getSelectionModel().getSelectedItems()) {
                    if (shouldSelect.test(s, t)) {
                        secondTable.getSelectionModel().select(t);
                    }
                }
            }
            selecting = false;
        });
        secondTable.getSelectionModel().getSelectedItems().addListener((Change<? extends T> c) -> {
            if (selecting) return ;
            selecting = true;
            firstTable.getSelectionModel().clearSelection();
            for (S s : firstTable.getItems()) {
                for (T t : secondTable.getSelectionModel().getSelectedItems()) {
                    if (shouldSelect.test(s, t)) {
                        firstTable.getSelectionModel().select(s);
                    }
                }
            }
            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;
//                }
//            }
//        });
        SelectionManager selectionManager = new SelectionManager();
        selectionManager.setUpMultiSelection(assembliesTable, partsTable,
                (assembly, part) -> assembly.partsProperty().contains(part)
        );

        return new SplitPane(assembliesTable, partsTable);
    }

这里需要注意的一点是,如果您使用相同的SelectionManager示例进行多个“多选”,它将使用相同的互斥锁。另一方面,不同的SelectionManager示例将有自己的互斥锁。这(或它的一些变体)应该使您能够相当容易地以任何需要的方式在许多表上配置它。

相关问题