【问题标题】:JavaFX TreeTableView - Prevent editing of unavailable cellsJavaFX TreeTableView - 防止编辑不可用的单元格
【发布时间】:2020-03-13 05:30:34
【问题描述】:

我有一个特定的 TreeTableView,它显示混合类型的分层树。这些类型不一定有重叠的列,因此某些行的列将为空。例如,考虑以下类:

public class Person {

    private final StringProperty nameProperty;
    private final StringProperty surnameProperty;

    public Person() {
        this.nameProperty = new SimpleStringProperty();
        this.surnameProperty = new SimpleStringProperty();
    }

    public StringProperty nameProperty() {
        return this.nameProperty;
    }

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

    public String getName() {
        return this.nameProperty.get();
    }

    public StringProperty surnameProperty() {
        return this.surnameProperty;
    }

    public void setSurname(String value) {
        this.surnameProperty.set(value);
    }

    public String getSurname() {
        return this.surnameProperty.get();
    }
}

public class Dog {

    private final StringProperty nameProperty;
    private final IntegerProperty ageProperty;
    private final StringProperty breedProperty;

    public Dog() {
        this.nameProperty = new SimpleStringProperty();
        this.ageProperty = new SimpleIntegerProperty();
        this.breedProperty = new SimpleStringProperty();
    }

    public StringProperty nameProperty() {
        return this.nameProperty;
    }

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

    public String getName() {
        return this.nameProperty.get();
    }

    public IntegerProperty ageProperty() {
        return this.ageProperty;
    }

    public void setAge(int value) {
        this.ageProperty.setValue(value);
    }

    public int getAge() {
        return this.ageProperty.get();
    }

    public StringProperty breedProperty() {
        return this.breedProperty;
    }

    public void setBreed(String breed) {
        this.breedProperty.set(breed);
    }

    public String getBreed() {
        return this.breedProperty.get();
    }
}

如果我按如下方式构造 TreeTableView:

TreeTableView<Object> treeTableView = new TreeTableView<>();
treeTableView.setEditable(true);

List<TreeTableColumn<Object, ?>> columns = treeTableView.getColumns();

TreeTableColumn<Object, String> nameColumn = new TreeTableColumn<>("Name");
nameColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("name"));
nameColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
columns.add(nameColumn);

TreeTableColumn<Object, String> surnameColumn = new TreeTableColumn<>("Surname");
surnameColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
surnameColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("surname"));
columns.add(surnameColumn);

TreeTableColumn<Object, Integer> ageColumn = new TreeTableColumn<>("Age");
ageColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn(new IntegerStringConverter()));
ageColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("age"));
columns.add(ageColumn);

TreeTableColumn<Object, String> breedColumn = new TreeTableColumn<>("Breed");
breedColumn.setCellFactory(TextFieldTreeTableCell.forTreeTableColumn());
breedColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("breed"));
columns.add(breedColumn);

TreeItem<Object> rootItem = new TreeItem<>();
treeTableView.setRoot(rootItem);
treeTableView.setShowRoot(false);

List<TreeItem<Object>> rootChildren = rootItem.getChildren();

Person john = new Person();
john.setName("John");
john.setSurname("Denver");
TreeItem<Object> johnTreeItem = new TreeItem<>(john);

rootChildren.add(johnTreeItem);

List<TreeItem<Object>> johnChildren = johnTreeItem.getChildren();

Dog charlie = new Dog();
charlie.setName("Charlie");
charlie.setAge(4);
charlie.setBreed("Labrador");
TreeItem<Object> charlieTreeItem = new TreeItem<>(charlie);
johnChildren.add(charlieTreeItem);

Dog daisy = new Dog();
daisy.setName("Daisy");
daisy.setAge(7);
daisy.setBreed("Bulldog");
TreeItem<Object> daisyTreeItem = new TreeItem<>(daisy);
johnChildren.add(daisyTreeItem);

我会得到一个看起来像这样的 TreeTableView:

对于包含 Person 对象的 TreeItems,Age 和 Breed 列是空的。但是,没有什么能阻止我为最上面的 Person 行编辑 Age 或 Breed 单元格。在其中一个单元格中设置一个值不会更改 Person 对象,但该值仍然像已提交一样在那里徘徊。

有什么方法可以防止这种情况发生吗?我知道我可以检查自定义 TreeTableCell 子类中的空值,并防止在 startEdit() 方法中开始编辑。但是,在某些情况下,空值是有效的,并且通过检查空值来阻止编辑并不是适用于所有情况的可行解决方案。此外,为每个数据类型和相应的列创建自定义 TreeTableCell 子类是很痛苦的。如果 TreeItemPropertyValueFactory 可以提供一种在特定单元格不存在值时中止编辑的方法,那就太好了。

【问题讨论】:

  • 这能回答你的问题吗? TreeTableView : setting a row not editable
  • 不,它没有。您所指的问题与整行的可编辑性有关,并将其与单元格的可编辑性同步。如果我使用 TreeItemPropertyValueFactory 并且它不返回某些单元格的值,我的问题与如何防止单元格被编辑有关。
  • 但基本相同......只需将单元格的可编辑性绑定到项目的类型或您想要的任何内容。这只不过是在此处将您的答案应用于您的问题。
  • 我猜你是对的。但是,走那条路会很混乱。我希望有一种更简洁的方法,使用 TreeItemPropertyValueFactory 可以在单元格没有值时提前退出单元格编辑。
  • valueFactory != cellFactory :) 您需要的是一个自定义单元格,它将自己的可编辑性绑定到您认为合适的任何标准。

标签: javafx treeview javafx-8 treetableview


【解决方案1】:

好的,我通过查看 TreeItemPropertyValueFactory 类本身来寻找灵感来拼凑一些东西。这为我提供了所需的功能,尽管我不确定它是否 100% 正确或使用它的含义。

它基本上归结为安装一个新的 cell-factory 来检查 cell-value-factory 的类型是否为TreeItemPropertyValueFactory。如果是这种情况,则会安装一个新的 cell-factory,它代表原始的 cell-factory,但会为 table-row 和 tree-item 属性添加侦听器。当TreeItem 更改时,我们获取行数据并查看是否可以访问所需的属性(通过为性能而缓存的PropertyReference)。如果我们不能(并且我们得到两个例外),我们假设无法访问该属性并将单元格的可编辑属性设置为 false。

public <S, T> void disableUnavailableCells(TreeTableColumn<S, T> treeTableColumn) {
    Callback<TreeTableColumn<S, T>, TreeTableCell<S, T>> cellFactory = treeTableColumn.getCellFactory();
    Callback<CellDataFeatures<S, T>, ObservableValue<T>> cellValueFactory = treeTableColumn.getCellValueFactory();
    if (cellValueFactory instanceof TreeItemPropertyValueFactory) {
        TreeItemPropertyValueFactory<S, T> valueFactory = (TreeItemPropertyValueFactory<S, T>)cellValueFactory;
        String property = valueFactory.getProperty();
        Map<Class<?>, PropertyReference<T>> propertyRefCache = new HashMap<>();
        treeTableColumn.setCellFactory(column -> {
            TreeTableCell<S, T> cell = cellFactory.call(column);
            cell.tableRowProperty().addListener((o1, oldRow, newRow) -> {
                if (newRow != null) {
                    newRow.treeItemProperty().addListener((o2, oldTreeItem, newTreeItem) -> {
                        if (newTreeItem != null) {
                            S rowData = newTreeItem.getValue();
                            if (rowData != null) {
                                Class<?> rowType = rowData.getClass();
                                PropertyReference<T> reference = propertyRefCache.get(rowType);
                                if (reference == null) {
                                    reference = new PropertyReference<>(rowType, property);
                                    propertyRefCache.put(rowType, reference);
                                }
                                try {
                                    reference.getProperty(rowData);
                                } catch (IllegalStateException e1) {
                                    try {
                                        reference.get(rowData);
                                    } catch (IllegalStateException e2) {
                                        cell.setEditable(false);
                                    }
                                }
                            }
                        }
                    });
                }
            });
            return cell;
        });
    }
}

对于问题中列出的示例,您可以在创建所有列后调用它:

...
columns.forEach(this::disableUnavailableCells);

TreeItem<Object> rootItem = new TreeItem<>();
treeTableView.setRoot(rootItem);
treeTableView.setShowRoot(false);
...

您会看到 Age 和 Breed 列的单元格现在对于 Person 条目是不可编辑的,而 Surname 列的单元格现在对于 Dog 条目是不可编辑的,这正是我们想要的。通用名称列的单元格对于所有条目都是可编辑的,因为这是 Person 和 Dog 对象之间的通用属性。

【讨论】:

    猜你喜欢
    • 2019-07-31
    • 2012-10-08
    • 2014-09-04
    • 1970-01-01
    • 1970-01-01
    • 2013-10-20
    • 1970-01-01
    • 1970-01-01
    • 2019-03-02
    相关资源
    最近更新 更多