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);
}
}
}
Это позволит вам выбрать редактор на основе имени объекта и типа компонента, к которому принадлежит объект...