JavaFX PropertyGrid/ редактор

Я пытаюсь написать компонент JavaFx, который обычно называется "редактором свойств" или "сеткой свойств". Свойство, являющееся парой имя-значение.

Я предполагаю, что лист свойств создан для этого, но я хотел бы использовать TreeTableView. Главным образом потому, что у меня есть вложенные свойства и в конечном итоге несколько столбцов.

Компонент справа - это именно то, чего я пытаюсь достичь. табличного дерева

Проблема, с которой я столкнулся в TreeTableView, заключается в том, что в CellFactory должна происходить настройка ячейки, что приводит к переключению типа элемента. Это решение делает вещи действительно негибкими.

Например, что произойдет, если строковое значение должно быть обновлено через TextField для данного свойства и через ComboBox для другого свойства?

Любое предложение приветствуется!

Относящиеся к делу вопросы: /questions/36804477/yachejki-javafx-8-customviewview-eto-zlo


Update1

Я попытался реализовать первое предложение @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);

Моя проблема в cellValueFactory, который должен возвращать ObservableValue. Что мне делать, если я хочу, чтобы этот столбец был редактируемым?

Я думаю, что EditableItem должен расширять свойство? Но тогда может ли мой DefaultEditableItem расширить SimpleStringProperty?

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 и 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);
        }
    }

}

Это позволит вам выбрать редактор на основе имени объекта и типа компонента, к которому принадлежит объект...

Другие вопросы по тегам