1

我正在尝试编写一个通常称为“属性编辑器”或“属性网格”的 JavaFx 组件。属性是名称-值对。

我想属性表是为此而制作的,但我想使用 TreeTableView。主要是因为我有嵌套的属性,最终有几列。

右边的组件正是我试图实现的。 树表视图

我在使用 TreeTableView 时遇到的问题是,必须在 CellFactory 中进行单元格自定义,这会导致项目类型的切换。这种解决方案使事情变得非常不灵活。

例如,如果字符串值必须通过给定属性的 TextField 和另一个属性的 ComboBox 更新,会发生什么情况?

任何建议都非常受欢迎!

相关问题:javafx-8-custom-listview-cells-its-evil


更新1

我试图实施@fabian 的第一个建议。

我有我的豆子:

public class PropertyItem {

private StringProperty name = new SimpleStringProperty("");

private EditableItem value;
...
}

EditableItem 的默认实现,通过在 TextField 中编辑字符串:

public class DefaultEditableItem implements EditableItem {

String value = "init value";
private TextField textField = new TextField();

public DefaultEditableItem(String value) {
    this.setValue(value);
}

// implementations of assignItem, removeItem, startEdit, cancelEdit,... as suggested for the cell behavior
}

我对 TableView 的实现:

PropertyItem rootProp = new PropertyItem("ROOT", new    DefaultEditableItem("test roots"));
TreeItem<PropertyItem> root = new TreeItem(rootProp);

// the name column is straightforward ...

// value column
TreeTableColumn<PropertyItem, EditableItem> valueColumn = new TreeTableColumn<>("VALUE");
valueColumn.setCellValueFactory(new Callback<TreeTableColumn.CellDataFeatures<PropertyItem, EditableItem>, ObservableValue<EditableItem>>() {
    @Override
    public ObservableValue<EditableItem> call(TreeTableColumn.CellDataFeatures<PropertyItem, EditableItem> cellData) {
            TreeItem<PropertyItem> treeItem = cellData.getValue();
            PropertyItem propertyItem = treeItem.getValue();
            // this will not compile...
            return propertyItem.value();                
    }
});

valueColumn.setCellFactory(new Callback<TreeTableColumn<PropertyItem, EditableItem>, TreeTableCell<PropertyItem, EditableItem>>() {
    @Override
    public TreeTableCell<PropertyItem, EditableItem> call(TreeTableColumn<PropertyItem, EditableItem> param) {
            return new EditingTreeTableCell();
    }
});
valueColumn.setOnEditCommit(...)

treeTableView.getColumns().addAll(nameColumn, valueColumn);
treeTableView.setEditable(true);

我的问题出在需要返回 ObservableValue 的 cellValueFactory 上。鉴于我希望此列可编辑,我应该怎么做?

我猜 EditableItem 必须扩展属性?但是,我的 DefaultEditableItem 可以扩展 SimpleStringProperty 吗?

4

1 回答 1

1

您可以在项目本身中存储有关如何编辑项目的信息(直接或通过允许您使用存储在项目中的合适键从地图或类似数据结构中检索它)。

例子:

public interface EditableItem {

    /**
     * Modify cell ui the way updateItem would do it, when the item is
     * added to the cell
     */
    void assignItem(EditingTreeTableCell<?, ?> cell);

    /**
     * Modify cell ui to remove the item the way it would be done in the updateItem method
     */
    void removeItem(EditingTreeTableCell<?, ?> cell);
}
public class EditingTreeTableCell<U, V> extends TreeTableCell<U, V> {

    @Override
    public void updateItem(V item, boolean empty) {
        boolean cleared = false;
        V oldItem = getItem();
        if (oldItem instanceof EditableItem) {
            ((EditableItem) oldItem).removeItem(this);
            cleared = true;
        }

        super.updateItem(item, empty);

        if (empty) {
            if (!cleared) {
                 setText("");
                 setGraphic(null);
            }
        } else {
             if (item instanceof EditableItem) {
                 ((EditableItem) item).assignItem(this);
             } else {
                 setText(Objects.toString(item, ""));
                 // or other default initialistation
             }
        }

    }

}

然而,由于这会增加项目的大小,您还可以根据属性所在的 bean 的类型和属性的名称来存储信息,也就是说,如果为bean属性name分配了 和 属性:

public interface CellEditor<U, V> {

    /**
     * Modify cell ui the way updateItem would do it, when the item is
     * added to the cell
     */
    void assignItem(EditorTreeTableCell<U, V> cell, V item);

    /**
     * Modify cell ui to remove the item the way it would be done in the updateItem method
     */
    void removeItem(EditorTreeTableCell<U, V> cell);
}
public class EditorTreeTableCell<U, V> extends TreeTableCell<U, V> {

    public EditorTreeTableCell(Map<Class, Map<String, CellEditor<U, ?>>> editors) {
        this.editors = editors;
    }

    private CellEditor<U, V> editor;
    private final Map<Class, Map<String, CellEditor<U, ?>>> editors;

    @Override
    public void updateIndex(int i) {
        if (editor != null) {
            editor.removeItem(this);
            editor = null;
        }
        ObservableValue<V> observable = getTableColumn().getCellObservableValue(i);
        if (observable instanceof ReadOnlyProperty) {
            ReadOnlyProperty prop = (ReadOnlyProperty) observable;
            String name = prop.getName();
            Object bean = prop.getBean();
            if (name != null && bean != null) {
                 Class cl = bean.getClass();
                 while (editor == null  && cl != null) {
                     Map<String, CellEditor<U, ?>> map = editors.get(cl);
                     if (map != null) {
                          editor = (CellEditor) map.get(name);
                     }
                     cl = cl.getSuperclass();
                 }
            }
        }

        super.updateIndex(i);
    }

    public void updateItem(V item, boolean empty) {
        super.updateItem();
        if (editor == null) {
             setGraphic(null);
             setText(Objects.toString(item, ""));
        } else {
             editor.assignItem(this, item);
        }
    }

}

这将允许您根据对象名称和对象所属的 bean 类型选择编辑器...

于 2016-11-28T12:21:33.860 回答