TreeTableView 禁用父行中的任何单元格

TreeTableView disable any cell in parent row

如何在 treetableview 的父行中禁用任何可编辑的单元格?请查看图片并检查示例代码。如果行是可扩展的(根行或子根行)

,我想很快禁用行可编辑

这张图片是正确的

但这不正确

**示例代码**

import javafx.application.Application;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class TreeTableExample extends Application {

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

    @Override
    @SuppressWarnings("unchecked")
    public void start(Stage stage) {

        HBox root = new HBox(createTable());
        Scene scene = new Scene(root);
        stage.setScene(scene);
        stage.setTitle("Using a TreeTableView");
        stage.show();
    }

    public TreeTableView createTable() {

        TreeTableView<Person> treeTable = new TreeTableView<>();
        treeTable.setEditable(true);

        Callback<TreeTableColumn<Person, String>, 
            TreeTableCell<Person, String>> cellFactory
                = (TreeTableColumn<Person, String> p) -> new EditingCell();

        TreeTableColumn<Person, String> firstName = new TreeTableColumn<>("First Name");
        firstName.setCellValueFactory(new TreeItemPropertyValueFactory<>("firstName"));
        firstName.setCellFactory(cellFactory);
        firstName.setOnEditCommit((TreeTableColumn.CellEditEvent<Person, String> event) -> {
            if(event.getNewValue()!=null)
                event.getRowValue().getValue().setFirstName(event.getNewValue());
        });

        TreeTableColumn<Person, String> lastName = new TreeTableColumn<>("Last Name");
        lastName.setCellValueFactory(new TreeItemPropertyValueFactory<>("lastName"));
        lastName.setCellFactory(cellFactory);
        lastName.setOnEditCommit((TreeTableColumn.CellEditEvent<Person, String> event) -> {
            if(event.getNewValue()!=null)
                event.getRowValue().getValue().setLastName(event.getNewValue());
        });

        treeTable.getColumns().addAll(firstName, lastName);
        TreeItem<Person> root = new TreeItem<>();
        for (int i = 0; i < 5; i++) {
            root.getChildren().add(new TreeItem<>(new Person()));
        }
        treeTable.setRoot(root);
        return treeTable;
    }

    public class Person {

        private SimpleStringProperty firstName;
        private SimpleStringProperty lastName;

        public Person(){
            firstName = new SimpleStringProperty(this, "firstName");
            lastName = new SimpleStringProperty(this, "lastName");
        };

        public String getFirstName() {
            return firstName.get();
        }

        public void setFirstName(String fName) {
            firstName.set(fName);
        }

        public String getLastName() {
            return lastName.get();
        }

        public void setLastName(String fName) {
            lastName.set(fName);
        }

    }

    class EditingCell extends TreeTableCell<Person, String> {

        private TextField textField;

        public EditingCell() {
        }

        @Override
        public void startEdit() {
            if (!isEmpty()) {
                super.startEdit();
                createTextField();
                setText(null);
                setGraphic(textField);
                textField.selectAll();
            }
        }

        @Override
        public void cancelEdit() {
            super.cancelEdit();

            setText((String) getItem());
            setGraphic(null);
        }

        @Override
        public void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);

            if (empty) {
                setText(null);
                setGraphic(null);
            } else if (isEditing()) {
                if(!getTreeTableView().getTreeItem(getIndex()).isLeaf())
                    setEditable(false);
                if (textField != null) {
                    textField.setText(getString());
                }
                setText(null);
                setGraphic(textField);
            } else {
                setText(getString());
                setGraphic(null);
            }
        }

        private void createTextField() {
            textField = new TextField(getString());
            textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
            textField.focusedProperty().addListener(
                    (ObservableValue<? extends Boolean> arg0,
                            Boolean arg1, Boolean arg2) -> {
                        if (!arg2) {
                            commitEdit(textField.getText());
                        }
                    });
        }

        private String getString() {
            return getItem() == null ? "" : getItem();
        }
    }
}

只需运行它并双击根项目

make-individual-cell-editable-in-javafx-tableview 我检查了该解决方案适用于 tableview 但不适用于 treetaleview。

似乎 TreeTableCell 在决定​​是否调用 startEdit() 之前没有正确检查它的 editable 属性。我认为这是一个错误。您可以通过在 startEdit() 方法中检查自己来解决它:

@Override
public void startEdit() {
    if (isEditable() && !isEmpty()) {
        super.startEdit();
        createTextField();
        setText(null);
        setGraphic(textField);
        textField.selectAll();
    }
}

现在在您的 updateItem() 方法中,您可以检查行中的当前树项目,并根据需要更新 editable

@Override
public void updateItem(String item, boolean empty) {
    super.updateItem(item, empty);

    TreeItem<Person> treeItem = getTreeTableRow().getTreeItem();
    setEditable(treeItem != null &&  treeItem.isLeaf());

    if (empty) {
        setText(null);
        setGraphic(null);
    } else if (isEditing()) {
        if(!getTreeTableView().getTreeItem(getIndex()).isLeaf())
            setEditable(false);
        if (textField != null) {
            textField.setText(getString());
        }
        setText(null);
        setGraphic(textField);
    } else {
        setText(getString());
        setGraphic(null);
    }
}

实际上我不同意 中的推理:核心 TreeTableCell 没有任何问题(它 确实 在实际开始编辑之前检查其可编辑性)-相反,自定义单元实现中的逻辑被破坏了。特别是,设置可编辑 属性:

的 updateItem 部分
} else if (isEditing()) {
    if(!getTreeTableView().getTreeItem(getIndex()).isLeaf())
        setEditable(false);

除了没有在任何地方将 editable 重置为 true 是不完整的(记住:单元格是 re-used),我们允许 super first 开始编辑并且只 启动后,它被禁用了。

通过无条件地在 updateItem 中设置可编辑性来修复此逻辑错误(在另一个答案中,为方便起见复制在这里):

super.updateItem(item, empty);

TreeItem<Person> treeItem = getTreeTableRow().getTreeItem();
setEditable(treeItem != null &&  treeItem.isLeaf());

另一个使用错误(如前所述)是在实际配置编辑器之前没有完全检查单元格状态。建议的修复 - 检查单元格的可编辑性 - 并不十分完整,因为 table/column 可编辑性也可能被禁用。考虑到这一点,我倾向于让 super 完成它的工作,并且只有在可编辑性实际发生变化时才配置编辑器,比如

super.startEdit();
// super changed state into editing 
if (isEditing()) {
   // create and install the textField
}