JavaFX树表格单元格中需要多个控件 [英] Multiple Controls needed in JavaFX Tree Table Cell

查看:148
本文介绍了JavaFX树表格单元格中需要多个控件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm needing different editable controls in a JavaFX TreeTableCell based on an enum attribute value of the row object. In different cases I need a DatePicker, a TextField, a CheckBox, a ComboBox, or a simple non editable Text field.

我已经扩展了TreeTableCell和重写updateItem来处理不同的情况,但这变得非常麻烦。

I've extended TreeTableCell and overridden the updateItem to handle different cases but that is getting very cumbersome.

是否可以创建一个自定义的CellFactory回调,以根据行对象的属性返回不同的子类TreeTableCells ?我该怎么做?

Is it possible to create a custom CellFactory Callback to return different subclassed TreeTableCells based on attributes of the row object? How might I go about doing this?

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版本,但我无法理解如何提交和更新对不同单元格的更改。我会发布更正的版本一旦找到解决方案

I've implemented an SSCCE version of James_D's method but am having trouble understanding how to commit and update changes to the different cells. I'll post the corrected version Once I find a solution

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

}

}


推荐答案

当用户展开并折叠树中的项目时,单元工厂返回的单元格将被 TreeTableView 重用或者滚动浏览数据等。因此,您返回的单元格必须能够处理所有情况,并且您不能返回仅处理特定行的单元格实例。

The cell that is returned by the cell factory will be reused by the TreeTableView as the user expands and collapses items in the tree, or scrolls through the data, etc. So the cell you return must be able to handle all cases, and you cannot return a cell instance that only handles specific rows.

如果要重构,则必须重构 updateItem(...)方法中的代码,您可以对所需的任何模块化程度进行重构。一个(可能是极端的)示例可能是:

If you want to refactor, you have to refactor the code in the updateItem(...) method, which you can do to any degree of modularity you want. A (perhaps extreme) example might be:

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);
    }
}

现在您的实际单元格实现减少为:

And now your actual cell implementation reduces to:

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(...)方法,你可以添加接口的方法,例如

If you need to implement the commitEdit(...) method in the cell, you can add a method to the interface, e.g.

public void updateFromControl(MyField myField) ;

有(我认为)明显的实施,例如

with (I think) the obvious implementations throughout, e.g.

public class DatePickerProvider implements EditingControlProvider {

    // existing code...

    @Override
    public void updateFromControl(MyField myField) {
        myField.setDate(datePicker.getValue());
    }
}

这篇关于JavaFX树表格单元格中需要多个控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆