JavaFX TableView 使用复选框来填充列表

标签 java checkbox javafx tableview

我对 JavaFX 相当陌生,我正在尝试在 JavaFX 中实现这一原则:我有一个填充了 Student 对象的 TableView。我希望第一列是一个复选框,通过它我可以选择每一行以对所选项目执行批量操作(如邮件应用程序中常见的情况)。

我认为我不应该将 SimpleBooleanProperty 添加到 Student 类,因为它仅在 View 层中使用,这就是为什么我认为我可以这样实现它:当选中复选框时,学生将被添加到列出选定的学生;当未选中时,它将被删除。这是一个好方法吗?

这是我到目前为止得到的代码(主要基于类似解决方案的复制粘贴):

    voornaamKolom.setCellValueFactory(new PropertyValueFactory<Student, String>("name"));
    familienaamKolom.setCellValueFactory(new PropertyValueFactory<Student, String>("fname"));
    promotorKolom.setCellValueFactory(new PropertyValueFactory<Student, String>("comment"));

    selectedKolom.setCellValueFactory(
            new Callback<TableColumn.CellDataFeatures<Student, Boolean>, ObservableValue<Boolean>>() {

                @Override
                public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<Student, Boolean> p) {
                    return new SimpleBooleanProperty(p.getValue() != null);
                }
            });

    selectedKolom.setCellFactory(
            new Callback<TableColumn<Student, Boolean>, TableCell<Student, Boolean>>() {

                @Override
                public TableCell<Student, Boolean> call(TableColumn<Student, Boolean> p) {
                    return new CheckBoxCell(studentenTabel);
                }

            });

    studentenTabel.getItems().setAll(getModel().getStudenten());

--

private class CheckBoxCell extends TableCell<Student, Boolean> {

    final CheckBox cellCheckBox = new CheckBox();

    CheckBoxCell(final TableView tblView) {

        cellCheckBox.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent t) {
                int selectedIndex = getTableRow().getIndex();

                if (!cellCheckBox.isSelected()) {
                    getModel().selectStudent(selectedIndex); // add to selectedStudents
                } else {
                    getModel().deselectStudent(selectedIndex); // remove from selectedStudents
                }
            }
        });
    }

    //Display button if the row is not empty
    @Override
    protected void updateItem(Boolean t, boolean empty) {
        super.updateItem(t, empty);
        if (!empty) {
            setGraphic(cellCheckBox);
        }
    }
}

此代码的主要问题是复选框未绑定(bind)到表行。例如。当我选择第二个项目并通过对另一个值进行排序来更改行顺序时,第二个项目仍然被选中,即使它代表另一个对象。当新行添加到表中时,其中一些也会被随机选择。

我知道这段代码可能很脏,就像我说的:我是 JavaFX 新手。任何帮助将不胜感激。

提前致谢!

最佳答案

在我看来,您的复选框列的数据类型应该是 Student ;即它是 TableColumn<Student, Student> 。这样做的原因是,您实际上呈现的是整个对象本身的 View :学生是否包含在选定学生的集合中。有点违反直觉,但它确实有效。

看看这个例子是否有帮助。我没有将数据很好地分离到您的代码暗示的模型中,但您也应该能够将其考虑在内。

import javafx.application.Application;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TableColumn.CellDataFeatures;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class SelectableTable extends Application {

    @Override
    public void start(Stage primaryStage) {
        TableView<Item> itemTable = new TableView<>();
        for (int i=1; i<=40; i++) {
            itemTable.getItems().add(new Item("Item "+i));
        }
        TableColumn<Item, String> nameCol = new TableColumn<>("Name");
        nameCol.setCellValueFactory(new PropertyValueFactory<>("name"));


        TableColumn<Item, Item> selectedCol = new TableColumn<>("Select");

        // Collection of items currently selected via checkboxes in the table 
        // This will be passed to the TableCell implementation.
        ObservableSet<Item> selectedItems = FXCollections.observableSet();

        selectedCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Item,Item>, ObservableValue<Item>>() {

            @Override
            public ObservableValue<Item> call(CellDataFeatures<Item, Item> data) {
                return new ReadOnlyObjectWrapper<>(data.getValue());
            }
        });

        selectedCol.setCellFactory(new Callback<TableColumn<Item, Item>, TableCell<Item, Item>>() {

            @Override
            public TableCell<Item, Item> call(
                    TableColumn<Item, Item> param) {
                return new CheckBoxCell(selectedItems);
            }
        });

        itemTable.getColumns().addAll(selectedCol, nameCol);

        Button displayButton = new Button("Display selected");
        displayButton.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                for (Item item : selectedItems) {
                    System.out.println(item.getName());
                }
            }
        });

        Button selectAllButton = new Button("Select all");
        selectAllButton.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                selectedItems.addAll(itemTable.getItems());
            }
        });

        Button selectNoneButton = new Button("Select none");
        selectNoneButton.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                selectedItems.clear();
            }
        });

        HBox buttons = new HBox(5);
        buttons.getChildren().addAll(selectAllButton, selectNoneButton, displayButton);

        BorderPane root = new BorderPane();
        root.setCenter(itemTable);
        root.setBottom(buttons);

        Scene scene = new Scene(root, 400, 400);
        primaryStage.setScene(scene);
        primaryStage.show();

    }

    public static class CheckBoxCell extends TableCell<Item, Item> {

        private final ObservableSet<Item> selectedItems ;
        private final CheckBox checkBox ;

        public CheckBoxCell(ObservableSet<Item> selectedItems) {
            this.selectedItems = selectedItems ;
            this.checkBox = new CheckBox() ;


            // listener to update the set of selected items when the 
            // check box is checked or unchecked:
            checkBox.setOnAction(new EventHandler<ActionEvent>() {

                @Override
                public void handle(ActionEvent event) {
                    if (checkBox.isSelected()) {
                        selectedItems.add(getItem());
                    } else {
                        selectedItems.remove(getItem());
                    }
                }
            });

            // listener to update the check box when the collection of selected
            // items changes:
            selectedItems.addListener(new SetChangeListener<Item>() {

                @Override
                public void onChanged(Change<? extends Item> change) {
                    Item item = getItem();
                    if (item != null) {
                        checkBox.setSelected(selectedItems.contains(item));
                    }
                }

            });
        }

        @Override
        public void updateItem(Item item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) {
                setGraphic(null);
            } else {
                checkBox.setSelected(selectedItems.contains(item));
                setGraphic(checkBox);
            }
        }
    }

    public static class Item {
        private final StringProperty name = new SimpleStringProperty(this, "name");
        public StringProperty nameProperty() {
            return name ;
        }
        public final String getName() {
            return name.get();
        }
        public final void setName(String name) {
            this.name.set(name);
        }
        public Item(String name) {
            setName(name);
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

关于JavaFX TableView 使用复选框来填充列表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23245227/

相关文章:

java - Java 中的组合框 - 空指针

java - 选择条形图类别标签

angular - MatCheckbox - Angular Material - 单元测试 - 如何单击以选择/取消选择所有行

java - 将字符串转换为 ObservableValue<String>

java - 如何在多个参数上动态构建 JDO 查询

java - 我可以使用正则表达式匹配特定字符的每三次出现吗?

javascript - 如果所有复选框都未选中,则禁用按钮,如果至少选中一个,则启用它

javascript - 如果模型计算结果为 true,则检查 angularjs 复选框

java - 将带有按钮的新行添加到 javafx tableview

java - JavaFX 中 ImageView 的交集