TreeTableView:设置一行不可编辑

TreeTableView : setting a row not editable

我想根据树中的级别控制树Table视图某些行的样式。如果此行是 Table 根的第一级子项的一部分,我使用 setRowFactory 并应用样式。样式工作正常,但我还想禁用单击这些行的复选框。我能够 setDisable(true) 但这也禁用了 TreeItem 的扩展并且 SetEditable(false) 似乎没有任何效果。

编辑:据我了解,Table 必须设置为可编辑,然后列默认可编辑。但是,如果我设置 TreeTableRow.setEditable(true);TreeTableRow.setEditable(false);,我将看不到任何效果。 setEditable 的描述似乎正是我想要的,但我无法那样使用它。

void javafx.scene.control.Cell.setEditable(boolean arg0)

setEditable public final void setEditable(boolean value)

Allows for certain cells to not be able to be edited. This is useful incases >where, say, a List has 'header rows' - it does not make sense forthe header rows >to be editable, so they should have editable set tofalse. Parameters:value - A boolean representing whether the cell is editable or not.If >true, the cell is editable, and if it is false, the cell can notbe edited.

主要:

public class TreeTableViewRowStyle extends Application {

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

    @Override
    public void start(Stage stage) throws Exception {

        // create the treeTableView and colums
        TreeTableView<Person> ttv = new TreeTableView<Person>();
        TreeTableColumn<Person, String> colName = new TreeTableColumn<>("Name");
        TreeTableColumn<Person, Boolean> colSelected = new TreeTableColumn<>("Selected");
        colName.setPrefWidth(100);
        ttv.getColumns().add(colName);
        ttv.getColumns().add(colSelected);
        ttv.setShowRoot(false);
        ttv.setEditable(true);

        // set the columns
        colName.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
        colSelected.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(colSelected));
        colSelected.setCellValueFactory(new TreeItemPropertyValueFactory<>("selected"));

        ttv.setRowFactory(table-> {
            return new TreeTableRow<Person>(){
                @Override
                public void updateItem(Person pers, boolean empty) {
                    super.updateItem(pers, empty);
                    boolean isTopLevel = table.getRoot().getChildren().contains(treeItemProperty().get());
                    if (!isEmpty()) {
                        if(isTopLevel){
                            setStyle("-fx-background-color:lightgrey;");
                            setEditable(false); //THIS DOES NOT SEEM TO WORK AS I WANT
                            //setDisable(true); //this would disable the checkbox but also the expanding of the tree
                        }else{                              
                            setStyle("-fx-background-color:white;");
                        }
                    }
                }
            };
        });


        // creating treeItems to populate the treetableview
        TreeItem<Person> rootTreeItem = new TreeItem<Person>();
        TreeItem<Person> parent1 = new TreeItem<Person>(new Person("Parent 1"));
        TreeItem<Person> parent2 = new TreeItem<Person>(new Person("Parent 1"));
        parent1.getChildren().add(new TreeItem<Person>(new Person("Child 1")));
        parent2.getChildren().add(new TreeItem<Person>(new Person("Child 2")));
        rootTreeItem.getChildren().addAll(parent1,parent2);


        ttv.setRoot(rootTreeItem);

        // build and show the window
        Group root = new Group();
        root.getChildren().add(ttv);
        stage.setScene(new Scene(root, 300, 300));
        stage.show();
    }
}

模范人物:

public class Person {
    private StringProperty name;
    private BooleanProperty selected;

    public Person(String name) {
        this.name = new SimpleStringProperty(name);
        selected = new SimpleBooleanProperty(false);
    }

    public StringProperty nameProperty() {
        return name;
    }

    public BooleanProperty selectedProperty() {
        return selected;
    }

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

    public void setSelected(boolean selected){
        this.selected.set(selected);
    }
}

如果您想禁用特定的 Cell,则在 CellFactory 而不是 RowFactory 中处理禁用逻辑。静态方法 forTreeTableColumn(..) 是一种方便快速使用的方法。但这不是唯一的方法。您仍然可以为 CheckBoxTreeTableCell 创建自己的工厂。

所以

colSelected.setCellFactory(CheckBoxTreeTableCell.forTreeTableColumn(colSelected));

如下设置细胞工厂,这应该适合你。

colSelected.setCellFactory(new Callback<TreeTableColumn<Person, Boolean>, TreeTableCell<Person, Boolean>>() {

    @Override
    public TreeTableCell<Person, Boolean> call(TreeTableColumn<Person, Boolean> column) {
        return new CheckBoxTreeTableCell<Person, Boolean>() {

            @Override
            public void updateItem(Boolean item, boolean empty) {
                super.updateItem(item, empty);
                boolean isTopLevel = column.getTreeTableView().getRoot().getChildren().contains(getTreeTableRow().getTreeItem());
                setEditable(!isTopLevel);
            }
        };
    }
});

基本问题是 none 可编辑(也不是像 CheckBoxXX 这样的伪可编辑)Tree/Table 单元格尊重它们所在行的可编辑性。我认为这是一个错误。

要克服这一点,您必须扩展(伪)可编辑单元格并使它们尊重行的可编辑性。伪编辑单元和真实编辑单元的确切实现是不同的。下面是在线示例,如果经常使用,您可以将它们设为顶级并重复使用。

CheckBoxTreeTableCell:子类化并覆盖 updateItem 以重新绑定其禁用的 属性 就像

colSelected.setCellFactory(c -> {
    TreeTableCell cell = new CheckBoxTreeTableCell() {

        @Override
        public void updateItem(Object item, boolean empty) {
            super.updateItem(item, empty);
            if (getGraphic() != null) {
                getGraphic().disableProperty().bind(Bindings
                        .not(
                              getTreeTableView().editableProperty()
                             .and(getTableColumn().editableProperty())
                             .and(editableProperty())
                             .and(getTreeTableRow().editableProperty())
                    ));
            }
        }

    };
    return cell;
});

对于真正的编辑单元格,f.i。 TextFieldTreeTableCell:如果行不可编辑,则覆盖 startEdit 和 return 而不调用 super

colName.setCellFactory(c -> {
    TreeTableCell cell = new TextFieldTreeTableCell() {

        @Override
        public void startEdit() {
            if (getTreeTableRow() != null && !getTreeTableRow().isEditable()) return;
            super.startEdit();
        }

    };
    return cell;
});

现在您可以像以前一样切换行的可编辑性,稍微更改了逻辑以保证在所有情况下都完全清理:

ttv.setRowFactory(table-> {
    return new TreeTableRow<Person>(){
        @Override
        public void updateItem(Person pers, boolean empty) {
            super.updateItem(pers, empty);
            // tbd: check for nulls!
            boolean isTopLevel = table.getRoot().getChildren().contains(treeItemProperty().get());
            if (!isEmpty() && isTopLevel) {
                //                        if(isTopLevel){
                setStyle("-fx-background-color:lightgrey;");
                setEditable(false); 
            }else{
                setEditable(true);
                setStyle("-fx-background-color:white;");

            }
        }
    };
});

您可以使用以下实用方法,而不是创建自定义 TreeTableCell 子类,该方法基本上在委托给原始细胞工厂的列上安装一个新的细胞工厂,但每当添加行可编辑性绑定时创建了一个单元格。

public <S, T> void bindCellToRowEditability(TreeTableColumn<S, T> treeTableColumn) {
    // Keep a handle on the original cell-factory.
    Callback<TreeTableColumn<S, T>, TreeTableCell<S, T>> callback = treeTableColumn.getCellFactory();
    // Install a new cell-factory that performs the delegation.
    treeTableColumn.setCellFactory(column -> {
        TreeTableCell<S, T> cell = callback.call(column);
        // Add a listener so that we pick up when a new row is set for the cell.
        cell.tableRowProperty().addListener((observable, oldRow, newRow) -> {
            // If the new row is non-null, we proceed.
            if (newRow != null) {
                // We get the cell and row editable-properties.
                BooleanProperty cellEditableProperty = cell.editableProperty();
                BooleanProperty rowEditableProperty = newRow.editableProperty();
                // Bind the cell's editable-property with its row's property.
                cellEditableProperty.bind(rowEditableProperty);
            }
        });
        return cell;
    });
}

然后您可以将 TreeTableView 的所有列设置为:

List<TreeTableColumn<S, ?>> columns = treeTableView.getColumns();
columns.forEach(this::bindCellToRowEditability);

您仍然需要自定义 TreeTableRow 检查它是否是顶级的,以便为行本身正确设置可编辑值。但是,在该行上设置可编辑值现在将确保该行中的所有单元格正确反映该行的可编辑值-属性.