我需要基于行对象的枚举属性值在JavaFX TreeTableCell中使用不同的可编辑控件。 在不同的情况下,我需要DatePicker,TextField,CheckBox,ComboBox或简单的不可编辑的文本字段。
我已经扩展了TreeTableCell并覆盖了updateItem以处理不同的情况,但这变得非常麻烦。
是否可以创建自定义CellFactory回调以根据行对象的属性返回不同的子类TreeTableCells?我该怎么做呢?
public class MyCellFactory implements Callback<TreeTableColumn<MyField,String>,TreeTableCell<MyField,String>> {
@Override
public TreeTableCell<MyField, String> call(TreeTableColumn<MyField, String> param) {
return new MyCell();
}
}
public class MyCell extends TreeTableCell<MyField, String> {
private TextField textField;
private DatePicker datePicker;
private CheckBox checkBox;
private Text text;
private ComboBox<String> comboBox;
public MyCell() {
super();
}
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || getTreeTableRow() == null) {
setText(null);
setGraphic(null);
} else {
MyField myField = (MyField) getTreeTableRow().getItem();
if (isEditing()) {
if (myField.getFieldType().equals(MyFieldType.CheckBox)) {
if (checkBox != null) {
checkBox.setSelected(getBoolean());
}
setText(null);
setGraphic(checkBox);
} else if (myField.getFieldType().equals(MyFieldType.Date)) {
if (datePicker != null) {
datePicker.setValue(getDate());
}
setText(null);
setGraphic(datePicker);
} else {
if (textField != null) {
textField.setText(getString());
}
setText(null);
setGraphic(textField);
}
//...
}
//...
}
我已经实现了James_D方法的SSCCE版本,但我无法理解如何提交和更新对不同单元格的更改。一旦找到解决方案,我会发布更正后的版本
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Control;
import javafx.scene.control.TextField;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class SampleApp extends Application {
public static void main(String[] args) {
Application.launch(args);
}
@SuppressWarnings("unchecked")
@Override
public void start(Stage primaryStage) throws Exception {
TreeItem<MyField> fooFields = new TreeItem<MyField>(new MyField("Foo", "Foo", null, false, null));
TreeItem<MyField> fooText = new TreeItem<MyField>(new MyField("fooText", "fooText", "text", true, null));
TreeItem<MyField> fooCheck = new TreeItem<MyField>(new MyField("fooCheck", "fooCheck", "check", true, null));
List<String> fooCombos = Arrays.asList("foo Combo 1", "foo Combo 2");
TreeItem<MyField> fooCombo = new TreeItem<MyField>(
new MyField("fooCombo", "foo Combo", "combo", true, fooCombos));
fooFields.getChildren().addAll(fooText, fooCheck, fooCombo);
TreeItem<MyField> barFields = new TreeItem<MyField>(new MyField("Bar", "Bar", null, false, null));
TreeItem<MyField> barText = new TreeItem<MyField>(new MyField("barText", "barText", "text", true, null));
TreeItem<MyField> barCheck = new TreeItem<MyField>(new MyField("barCheck", "barCheck", "check", true, null));
List<String> barCombos = Arrays.asList("bar Combo 1", "bar Combo 2");
TreeItem<MyField> barCombo = new TreeItem<MyField>(
new MyField("barCombo", "bar Combo", "combo", true, barCombos));
barFields.getChildren().addAll(barText, barCheck, barCombo);
TreeItem<MyField> hiddenRoot = new TreeItem<MyField>(new MyField("hidden", "hidden", null, false, null));
hiddenRoot.getChildren().addAll(fooFields, barFields);
TreeTableView<MyField> treeTable = new TreeTableView<>(hiddenRoot);
treeTable.setEditable(true);
treeTable.setPrefWidth(400);
treeTable.setShowRoot(false);
TreeTableColumn<MyField, String> nameCol = new TreeTableColumn<MyField, String>("Name");
nameCol.setPrefWidth(150);
nameCol.setCellValueFactory(new TreeItemPropertyValueFactory<MyField, String>("name"));
TreeTableColumn<MyField, String> valueCol = new TreeTableColumn<MyField, String>("Value");
valueCol.setPrefWidth(250);
valueCol.setCellValueFactory(new TreeItemPropertyValueFactory<MyField, String>("value"));
valueCol.setCellFactory(new MyFieldCellFactory());
treeTable.getColumns().addAll(nameCol, valueCol);
HBox root = new HBox(treeTable);
root.setStyle("-fx-padding: 10;" + "-fx-border-style: solid inside;" + "-fx-border-width: 2;"
+ "-fx-border-insets: 5;" + "-fx-border-radius: 5;" + "-fx-border-color: blue;");
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("Multi Control Tree Table View");
primaryStage.show();
}
public class MyField {
private String name;
private String value;
public String fieldType;
public boolean isEditable;
public List<String> comboVals;
public MyField(String name, String value, String fieldType, boolean isEditable, List<String> comboVals) {
super();
this.name = name;
this.value = value;
this.fieldType = fieldType;
this.isEditable = isEditable;
this.comboVals = comboVals;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getFieldType() {
return fieldType;
}
public void setFieldType(String fieldType) {
this.fieldType = fieldType;
}
public List<String> getComboVals() {
return comboVals;
}
public void setComboVals(List<String> comboVals) {
this.comboVals = comboVals;
}
public boolean isEditable() {
return isEditable;
}
public void setEditable(boolean isEditable) {
this.isEditable = isEditable;
}
}
public class MyFieldCellFactory
implements Callback<TreeTableColumn<MyField, String>, TreeTableCell<MyField, String>> {
@Override
public TreeTableCell<MyField, String> call(TreeTableColumn<MyField, String> param) {
return new MyFieldCell();
}
}
public class MyFieldCell extends TreeTableCell<MyField, String> {
private MyEditingControlProvider controlProvider = new MyCellEditingControlProvider();
public MyFieldCell() {
super();
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setGraphic(null);
} else {
MyField myField = getTreeTableRow().getItem();
setText(null);
setGraphic(controlProvider.getControl(myField));
}
}
protected void commitEdit() {
super.commitEdit(getItem());
MyField myField = getTreeTableRow().getItem();
controlProvider.updateFromControl(myField);
}
}
public interface MyEditingControlProvider {
public Control getControl(MyField field);
public void updateFromControl(MyField field);
}
public class MyCellEditingControlProvider implements MyEditingControlProvider {
private Map<String, MyEditingControlProvider> providers;
public MyCellEditingControlProvider() {
providers = new HashMap<>();
providers.put("check", new CheckProvider());
providers.put("combo", new ComboProvider());
providers.put("text", new TextProvider());
}
@Override
public Control getControl(MyField field) {
if (field == null || field.getFieldType() == null) {
return null;
} else {
return providers.get(field.getFieldType()).getControl(field);
}
}
@Override
public void updateFromControl(MyField field) {
providers.get(field.getFieldType()).updateFromControl(field);
}
}
public class CheckProvider implements MyEditingControlProvider {
private CheckBox checkBox;
@Override
public Control getControl(MyField field) {
if (checkBox == null) {
createCheckBox(field);
}
return checkBox;
}
private void createCheckBox(MyField field) {
checkBox = new CheckBox("Check");
checkBox.setSelected(getBoolean(field));
}
private Boolean getBoolean(MyField field) {
return field.getValue() == null ? false : convertYNToBoolean(field.getValue());
}
private Boolean convertYNToBoolean(String val) {
if (val != null && val.equals("Y")) {
return true;
} else {
return false;
}
}
private String convertBooleanToYN(Boolean val) {
if (val) {
return "Y";
} else {
return "N";
}
}
@Override
public void updateFromControl(MyField field) {
field.setValue(convertBooleanToYN(checkBox.isSelected()));
}
}
public class ComboProvider implements MyEditingControlProvider {
private ComboBox<String> comboBox;
@Override
public Control getControl(MyField field) {
if (comboBox == null) {
createComboBox(field);
}
return comboBox;
}
private void createComboBox(MyField field) {
comboBox = new ComboBox<String>();
comboBox.setEditable(true);
resetBox(field);
}
private void resetBox(MyField field) {
comboBox.getItems().clear();
comboBox.getItems().addAll(field.getComboVals());
}
@Override
public void updateFromControl(MyField field) {
field.setValue(comboBox.getValue());
}
}
public class TextProvider implements MyEditingControlProvider {
private TextField textField;
@Override
public Control getControl(MyField field) {
if (textField == null) {
createTextField(field);
}
return textField;
}
private void createTextField(MyField field) {
textField = new TextField(field.getValue());
}
@Override
public void updateFromControl(MyField field) {
field.setValue(textField.getText());
}
}
}
答案 0 :(得分:1)
当用户展开并折叠树中的项目或滚动数据等时,单元工厂返回的单元格将由TreeTableView
重用。因此,您返回的单元格必须能够处理所有情况,并且您不能返回仅处理特定行的单元格实例。
如果要重构,则必须使用updateItem(...)
方法重构代码,您可以对任何程度的模块化进行重构。一个(也许是极端的)例子可能是:
public interface EditingControlProvider {
public Control getControl(MyField myField);
}
有一些特定的实现:
public class DatePickerProvider implements EditingControlProvider {
private DatePicker datePicker ;
@Override
public Control getControl(MyField myField) {
if (datePicker == null) {
datePicker = new DatePicker();
}
datePicker.setValue(myField.getDate());
return datePicker ;
}
}
和其他控件类似。
然后你可以做
public class CellEditingControlProvider implements EditingControlProvider {
private Map<MyFieldType, EditingControlProvider> providers ;
public CellEditingControlProvider() {
providers = new HashMap<>();
providers.put(MyFieldType.CheckBox, new CheckBoxProvider());
providers.put(MyFieldType.Date, new DatePickerProvider());
// etc...
}
@Override
public Control getControl(MyField myField) {
return providers.get(myField.getFieldType()).getControl(myField);
}
}
现在您的实际单元格实现简化为:
public class MyCell extends TreeTableCell<MyField, String> {
private EditingControlProvider controlProvider = new CellEditingControlProvider();
public MyCell() {
super();
}
@Override
public void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (empty || getTreeTableRow() == null) {
setText(null);
setGraphic(null);
} else {
MyField myField = (MyField) getTreeTableRow().getItem();
if (isEditing()) {
setText(null);
setGraphic(controlProvider.getControl(myField));
}
//...
}
//...
}
}
如果您需要在单元格中实现commitEdit(...)
方法,则可以向界面添加方法,例如
public void updateFromControl(MyField myField) ;
(我认为)明显的实施,例如
public class DatePickerProvider implements EditingControlProvider {
// existing code...
@Override
public void updateFromControl(MyField myField) {
myField.setDate(datePicker.getValue());
}
}