JavaFX根据复选框状态禁用TableColumn

时间:2017-09-19 01:28:49

标签: javafx java-8 javafx-8 javafx-tableview javafx-bindings

我希望仅在选中TableColumn<CustomObject, String> tableColumn复选框时,根据CustomObject中的字段值停用TableColumn<CustomObject, Boolean> tableColumnTwo。我可以在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("");
                                         }
                                     }
                                 }
                             };
                         }

                     });

2 个答案:

答案 0 :(得分:2)

您可以在复选框上添加一个监听器,选中此复选框将导致表刷新。

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将保持不变,并且会在选中/取消选中复选框时调用。

答案 1 :(得分:1)

通常,我们希望每当有关基础数据发生变化的通知时,都会更新单元格。为了确保更改项目属性的数据触发通知,我们需要一个包含我们感兴趣的属性的提取器的列表,如:

 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
 * https://stackoverflow.com/q/46290417/203657
 * 
 * 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());
}