2017-08-21 34 views
0

我需要基于行对象的枚举属性值的JavaFX TreeTableCell中的不同可编辑控件。 在不同的情况下,我需要一个DatePicker,一个TextField,一个CheckBox,一个ComboBox或一个简单的不可编辑的文本字段。JavaFX树表格中需要多个控件单元格

我已经扩展了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()); 
    } 

} 

} 
+1

*“是否有可能要创建自定义CellFactory回调返回根据行对象的属性不同的子类TreeTableCells?” * 号:因为这是由电池厂家提供的电池将用于被重用不同的项目,例如当用户展开/折叠表格中的节点或用户滚动时。所以返回的单元格必须能够处理所有情况。您的一般结构是正确的:您可能想要将控件的选择分解到单独的类中以简化代码。 –

回答

1

由细胞工厂返回将被作为TreeTableView用户通过数据扩展,并在树倒塌的物品,或滚动重复使用的电池等等。所以你返回的单元必须能够处理所有的情况,并且你不能返回只处理特定行的单元实例。

如果您想重构,您必须重构updateItem(...)方法中的代码,您可以对其进行任何程度的模块化。 A(也许极端)例如可以是:

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()); 
    } 
} 
+0

我有很多重构,但初始结果非常适合这种方法。谢谢你的深思熟虑的回应!一旦我完成了这一切,我会更新。 – ktdh

相关问题