Search code examples
javafx-8

How can I avoid changing a single CheckBox entry in a TableView. All other CheckBox Entries should be changable


How can I avoid changing a single CheckBox entry in a TableView. All other CheckBox Entries should be changable. It should not possible do deselect a specific Entry.

Eclipse IDE for Java Developers Version: Photon Release (4.8.0) Build id: 20180619-1200 OS: Windows 10, v.10.0, x86_64 / win32 Java version: 1.8.0_1

    package checkbox;

    import javafx.application.Application;
    import javafx.beans.property.SimpleBooleanProperty;
    import javafx.beans.value.ChangeListener;
    import javafx.beans.value.ObservableValue;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.geometry.Pos;
    import javafx.scene.Group;
    import javafx.scene.Scene;
    import javafx.scene.control.TableCell;
    import javafx.scene.control.TableColumn;
    import javafx.scene.control.TableColumn.CellDataFeatures;
    import javafx.scene.control.TableView;
    import javafx.scene.control.cell.CheckBoxTableCell;
    import javafx.scene.control.cell.PropertyValueFactory;
    import javafx.stage.Stage;
    import javafx.util.Callback;

    public class TableCellFactorySample extends Application {
        public static final String DONT_MISS = "Love";

        private void init(Stage primaryStage) {
            Group root = new Group();
            primaryStage.setScene(new Scene(root));
            final ObservableList<Needs> data = FXCollections.observableArrayList(new Needs(true, "Gold"),
                    new Needs(false, "SUV"), new Needs(true, DONT_MISS), new Needs(true, "Diamonds"),
                    new Needs(false, "Boot"));

            TableColumn<Needs, Boolean> neededColumn = new TableColumn<>("Need Things");
            neededColumn.setMinWidth(50);
            neededColumn.setCellValueFactory(
                    new Callback<TableColumn.CellDataFeatures<Needs, Boolean>, ObservableValue<Boolean>>() {

                        @Override
                        public ObservableValue<Boolean> call(CellDataFeatures<Needs, Boolean> param) {
                            Needs need = param.getValue();
                            SimpleBooleanProperty needBooleanProperty = new SimpleBooleanProperty(need.isNeeded());
                            needBooleanProperty.addListener(new ChangeListener<Boolean>() {

                                @Override
                                public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue,
                                        Boolean newValue) {
                                    if (need.getName().equals(DONT_MISS)) {
                                        System.err.println("Avoid changing the Checkbox");
                                    } else {
                                        System.err.println("Changing");
                                    }
                                    System.err.println(need.getName() + " / " + needBooleanProperty + "\n");
                                }
                            });
                            return needBooleanProperty;
                        }
                    });

            neededColumn.setCellFactory(//
                    new Callback<TableColumn<Needs, Boolean>, TableCell<Needs, Boolean>>() {
                        @Override
                        public TableCell<Needs, Boolean> call(TableColumn<Needs, Boolean> abbreviation) {
                            CheckBoxTableCell<Needs, Boolean> checkBoxTableCell = new CheckBoxTableCell<Needs, Boolean>();
                            checkBoxTableCell.setAlignment(Pos.CENTER);
                            return checkBoxTableCell;
                        }
                    });

            TableColumn<Needs, String> nameColumn = new TableColumn<>("Name");
            nameColumn.setCellValueFactory(new PropertyValueFactory<Needs, String>("name"));

            TableView<Needs> tableView = new TableView<>();
            tableView.setItems(data);
            tableView.setEditable(true);
            tableView.getColumns().add(neededColumn);
            tableView.getColumns().add(nameColumn);
            root.getChildren().add(tableView);
        }

        @Override
        public void start(Stage primaryStage) throws Exception {
            init(primaryStage);
            primaryStage.show();
        }

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


    package checkbox;

    public class Needs {
        private boolean needed;
        private String name;

        Needs(boolean needed, String name) {
            this.needed = needed;
            this.name = name;
        }

        public boolean isNeeded() {
            return needed;
        }

        public void setNeeded(boolean needed) {
            this.needed = needed;
        }

        public String getName() {
            return this.name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return this.name;
        }
    }

Solution

  • You can get the desired result with multiple solutions. Two of them are as below:

    Solution#1: Reset the value if you dont want to change

    neededColumn.setCellValueFactory(
            new Callback<TableColumn.CellDataFeatures<Needs, Boolean>, ObservableValue<Boolean>>() {
    
                @Override
                public ObservableValue<Boolean> call(CellDataFeatures<Needs, Boolean> param) {
                    Needs need = param.getValue();
                    SimpleBooleanProperty needBooleanProperty = new SimpleBooleanProperty(need.isNeeded());
                    ChangeListener<Boolean> listener = new ChangeListener<Boolean>() {
                        @Override
                        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                            if (need.getName().equals(DONT_MISS)) {
                                System.err.println("Avoid changing the Checkbox");
                                // RESET THE VALUE WHEN YOU DONT WANT TO CHANGE
                                Platform.runLater(() -> {
                                    needBooleanProperty.removeListener(this);
                                    needBooleanProperty.set(oldValue);
                                    needBooleanProperty.addListener(this);
                                });
                            } else {
                                System.err.println("Changing");
                            }
                            System.err.println(need.getName() + " / " + needBooleanProperty + "\n");
                        }
                    };
                    needBooleanProperty.addListener(listener);
                    return needBooleanProperty;
                }
            });
    

    Solution#2: Dont allow to interact with checkbox if you dont want to change

    neededColumn.setCellFactory(//
                    new Callback<TableColumn<Needs, Boolean>, TableCell<Needs, Boolean>>() {
                        @Override
                        public TableCell<Needs, Boolean> call(TableColumn<Needs, Boolean> abbreviation) {
                            CheckBoxTableCell<Needs, Boolean> checkBoxTableCell = new CheckBoxTableCell<Needs, Boolean>() {
                                @Override
                                public void updateItem(Boolean item, boolean empty) {
                                    super.updateItem(item, empty);
                                    setDisable(false);
                                    if (getTableRow() != null && !empty) {
                                        Needs needs = getTableView().getItems().get(getIndex());
                                        if (needs.getName().equals(DONT_MISS)) {
                                            setDisable(true);
                                        }
                                    }
                                }
                            };
                            checkBoxTableCell.setAlignment(Pos.CENTER);
                            return checkBoxTableCell;
                        }
                    });