JavaFX TableView使用复选框填充列表

时间:2014-04-23 13:05:38

标签: java checkbox javafx tableview

我是JavaFX的新手,我试图在JavaFX中完成这个原则:我已经有一个填充了Student对象的TableView。我希望第一列成为一个复选框,我可以使用该复选框选择每一行以对所选项执行批量操作(如邮件应用程序中常见的那样)。

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

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

    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);
        }
    }
}

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

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

提前致谢!

2 个答案:

答案 0 :(得分:2)

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

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

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);
    }
}

答案 1 :(得分:0)

您可以实施getModel().selectStudent(selectedObject);getModel().deselectStudent(selectedObject);代替。在这里,selectedObject应该是对象本身而不仅仅是索引。模型应指向对象而不是索引。

最好记住,您可以使模型指向您创建的确切对象。您只需要确保指向正确的对象。