JavaFX 根据复选框状态禁用 TableColumn

JavaFX Disable TableColumn based on checkbox state

我希望仅在选中 TableColumn<CustomObject, Boolean> tableColumnTwo 复选框时基于 CustomObject 中的字段值禁用 TableColumn<CustomObject, String> tableColumn。我可以禁用 public void updateItem(String s, boolean empty) 内的文本框,但不确定如何检查 updateItem 内复选框的状态 下面是相关的代码片段,如果有人能阐明这一点,我们将不胜感激

@FXML 
private TableColumn<CustomObject, Boolean> tableColumnTwo;
@FXML 
private TableColumn<CustomObject, String> tableColumn;

tableColumn.setCellFactory(
                     new Callback<TableColumn<CustomObject, String>, TableCell<CustomObject, String>>() {

                         @Override
                         public TableCell<CustomObject, String> call(TableColumn<CustomObject, String> paramTableColumn) {
                             return new TextFieldTableCell<CustomObject, String>(new DefaultStringConverter()) {
                                 @Override
                                 public void updateItem(String s, boolean empty) {
                                     super.updateItem(s, empty);
                                     TableRow<CustomObject> currentRow = getTableRow();
                                     if(currentRow.getItem() != null && !empty) {
                                         if (currentRow.getItem().getPetrified() == false) { // Need to check if checkbox is checked or not
                                             setDisable(true);
                                             setEditable(false);
                                             this.setStyle("-fx-background-color: red");
                                         } else {
                                             setDisable(false);
                                             setEditable(true);
                                                                                             setStyle("");
                                         }
                                     }
                                 }
                             };
                         }

                     });

通常,我们希望单元格在收到有关基础数据更改的通知时得到更新。为了确保在更改项目的 属性 时数据会触发通知,我们需要一个列表,其中包含我们感兴趣的属性的提取器,例如:

 ObservableList<CustomObject> data = FXCollections.observableArrayList(
      c ->  new Observable[] {c.petrifiedProperty()}
 );

有了这个,每当修饰的 属性 发生变化时,列表就会触发更新类型的列表变化。

不幸的是,这还不够,因为 bug in fx:当从基础项目接收到类型更新的 listChange 时,单元格不会更新。一个肮脏的方法(阅读:一旦错误被修复就不要使用,它正在使用紧急 api!)是在项目上安装一个监听器并在收到更新时调用 table.refresh()

一个例子:

import java.util.logging.Logger;

//import de.swingempire.fx.util.FXUtils;
import javafx.application.Application;
import javafx.beans.Observable;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.converter.DefaultStringConverter;

/**
 * CheckBoxTableCell: update editable state of one column based of  
 * the boolean in another column
 * 
 * 
 * Bug in skins: cell not updated on listChange.wasUpdated
 * 
 * reported as
 * https://bugs.openjdk.java.net/browse/JDK-8187665
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class TableViewUpdateBug extends Application {


    /**
     * TableCell that updates state based on another value in the row.
     */
    public static class DisableTextFieldTableCel extends TextFieldTableCell {

        public DisableTextFieldTableCel() {
            super(new DefaultStringConverter());
        }

        /**
         * Just to see whether or not this is called on update notification
         * from the items (it's not)
         */
        @Override
        public void updateIndex(int index) {
            super.updateIndex(index);
//            LOG.info("called? " + index);
        }

        /**
         * Implemented to change background based on 
         * visible property of row item.
         */
        @Override
        public void updateItem(Object item, boolean empty) {
            super.updateItem(item, empty);
            TableRow<TableColumn> currentRow = getTableRow();
            boolean editable = false;
            if (!empty && currentRow != null) {
                TableColumn column = currentRow.getItem();
                if (column != null) {
                    editable = column.isVisible();
                }
            }
            if (!empty) {
                setDisable(!editable);
                setEditable(editable);
                if (editable) {
                    this.setStyle("-fx-background-color: red");

                } else {
                    this.setStyle("-fx-background-color: green");
                }
            } else {
                setStyle("-fx-background-color: null");
            }
        }

    }

    @Override
    public void start(Stage primaryStage) {
        // data: list of tableColumns with extractor on visible property
        ObservableList<TableColumn> data = FXCollections.observableArrayList(
                c ->  new Observable[] {c.visibleProperty()});

        data.addAll(new TableColumn("first"), new TableColumn("second"));

        TableView<TableColumn> table = new TableView<>(data);
        table.setEditable(true);

        // hack-around: call refresh
        data.addListener((ListChangeListener) c -> {
            boolean wasUpdated = false;
            boolean otherChange = false;
            while(c.next()) {
                if (c.wasUpdated()) {
                    wasUpdated = true;
                } else {
                    otherChange = true;
                }

            }
            if (wasUpdated && !otherChange) {
                table.refresh();
            }
            //FXUtils.prettyPrint(c);
        });
        TableColumn<TableColumn, String> text = new TableColumn<>("Text");
        text.setCellFactory(c -> new DisableTextFieldTableCel()); 
        text.setCellValueFactory(new PropertyValueFactory<>("text"));

        TableColumn<TableColumn, Boolean> visible = new TableColumn<>("Visible");
        visible.setCellValueFactory(new PropertyValueFactory<>("visible"));
        visible.setCellFactory(CheckBoxTableCell.forTableColumn(visible));

        table.getColumns().addAll(text, visible);

        BorderPane root = new BorderPane(table);
        Scene scene = new Scene(root, 300, 150);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

    @SuppressWarnings("unused")
    private static final Logger LOG = Logger
            .getLogger(TableViewUpdateBug.class.getName());
}

您可以在复选框上添加侦听器,选中后会导致 table 刷新。

data = FXCollections.observableArrayList(new Callback<CustomObject, Observable[]>() {

            @Override
            public Observable[] call(CustomObject param) {
                return new Observable[]{param.petrifiedProperty()};
            }
    });


data.addListener(new ListChangeListener<CustomObject>() {

        @Override
        public void onChanged(ListChangeListener.Change<? extends CustomObject> c) {
            while (c.next()) {
                if (c.wasUpdated()) {
                    tableView.setItems(null); 
                    tableView.layout(); 
                    tableView.setItems(FXCollections.observableList(data)); 
                }
            }
        }
    });

您的 cellFactory 将保持不变,并在复选框为 checked/unchecked 时被调用。