JavaFX树表格单元格中需要多个控件

时间:2017-08-21 13:07:41

标签: javafx

我需要基于行对象的枚举属性值在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());
    }

}

}

1 个答案:

答案 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());
    }
}