JavaFX PropertyGrid/Editor
JavaFX PropertyGrid/Editor
我正在尝试编写一个通常称为 "property editor" 或 "property grid" 的 JavaFx 组件。 属性 是一对 name-value。
我想 property sheet 就是为此而设计的,但我想使用 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);
我的问题出在需要 return ObservableValue 的 cellValueFactory 上。我想让这个栏目可以编辑,怎么办?
我想 EditableItem 必须扩展 属性?但是,我的 DefaultEditableItem 是否可以扩展 SimpleString属性?
您可以在项目本身中存储有关应如何编辑项目的信息(直接或允许您使用存储在项目中的合适密钥从地图或类似数据结构中检索它)。
示例:
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);
}
}
}
这将允许您 select 基于对象名称和对象所属 bean 类型的编辑器...
我正在尝试编写一个通常称为 "property editor" 或 "property grid" 的 JavaFx 组件。 属性 是一对 name-value。
我想 property sheet 就是为此而设计的,但我想使用 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);
我的问题出在需要 return ObservableValue 的 cellValueFactory 上。我想让这个栏目可以编辑,怎么办?
我想 EditableItem 必须扩展 属性?但是,我的 DefaultEditableItem 是否可以扩展 SimpleString属性?
您可以在项目本身中存储有关应如何编辑项目的信息(直接或允许您使用存储在项目中的合适密钥从地图或类似数据结构中检索它)。
示例:
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);
}
}
}
这将允许您 select 基于对象名称和对象所属 bean 类型的编辑器...