2017-08-21 76 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

相關問題