如何使用不同类型的可编辑行创建JavaFX Tableview?

时间:2016-01-04 17:00:12

标签: javafx tableview javafx-8

我试图用JvafaFX创建一个表格视图,它将显示一个摄像机参数列表。其中一些参数是可编辑的,有些则不可编辑,有些参数受限于其他值为自由文本输入的值列表。我已设置表以在键,值类型显示中显示参数,其中一列用于参数名称,另一列用于值。

使用从背景数据模型生成的可观察列表设置表的值:

propertyNamesColumn.setCellValueFactory(cellData -> cellData.getValue().getName());
propertyValuesColumn.setCellValueFactory(cellData -> cellData.getValue().getValue());

该模型还包含属性是否应该是可编辑的以及它可能包含的可能值,这些值当前存储在两个不同的字段中(我不确定这是否是最佳方式)因此有4个字段总。

设置表格的行时,我想制作那些应该可编辑的行(根据模型中的值),可以通过包含可能值列表中的值或文本字段的选项框进行编辑。

但是我不确定我需要实现什么才能实现这一点,我尝试扩展ChoiceBoxTableCell类以添加此逻辑,但表格单元格甚至永远不会变得可编辑。

我非常确定选择框单元类型的扩展或单元工厂应该能够做到这一点,但我不知道如何。

感谢您的帮助。

1 个答案:

答案 0 :(得分:4)

我认为要这样做,您需要创建一个通用模型Parameter类,并将其用作表的类型。你可以将它抽象化并定义一个抽象的getEditor()方法(或者将编辑器工厂委托给另一个类,但我会尽量保持这个简单)。然后定义根据需要创建不同编辑器的子类。

这看起来像这样:

public abstract class Parameter<T> {

    private final BooleanProperty editable = new SimpleBooleanProperty();

    private final ObjectProperty<T> value = new SimpleObjectProperty<>();

    private final String name ;

    public Parameter(String name, T value, boolean editable) {
        this.name = name ;
        setValue(value);
        setEditable(editable);
    }

    public Parameter(String name, T value) {
        this(name, value, true);
    }

    public String getName() {
        return name ;
    }

    public ObjectProperty<T> valueProperty() {
        return value ;
    }

    public T getValue() {
        return valueProperty().get();
    }

    public void setValue(T value) {
        valueProperty().set(value);
    }

    public BooleanProperty editableProperty() {
        return editable ;
    }

    public boolean isEditable() {
        return editableProperty().get() ;
    }

    public void setEditable(boolean editable) {
        editableProperty().set(editable);
    }

    public abstract Node getEditor() ;

}

那么对于“免费”字符串,你可能会有这样一个简单的实现:

public class StringParameter extends Parameter<String> {

    private final TextField editor ;

    public StringParameter(String name, String value) {
        super(name, value);
        editor = new TextField();
        editor.textProperty().bindBidirectional(valueProperty());
    }

    @Override
    public Node getEditor() { 
        return editor ;
    }

}

对于一个微调器来说可能是这样的:

public class BoundIntegerParameter extends Parameter<Integer> {

    private final Spinner<Integer> editor ;

    public BoundIntegerParameter(int min, int max, String name, int value) {
        super(name, value);
        editor = new Spinner<>(min, max, value);
        editor.setEditable(true);
        editor.getValueFactory().valueProperty().bindBidirectional(valueProperty());
    }

    @Override
    public Node getEditor() {
        return editor ;
    }

}

对于“修复列表”,您可以类似地实现FixedStringParameter,其中包含字符串列表,其getEditor方法返回ComboBox。另一种固定选择的方法可能是使用Enum类型:这可能看起来像

public class EnumParameter<E extends Enum<E>> extends Parameter<E> {

    private final ComboBox<E> editor ;

    public EnumParameter(String name, E value) {
        super(name, value);
        editor = new ComboBox<>();
        @SuppressWarnings("unchecked")
        Class<E> type = (Class<E>) value.getClass();
        E[] values = type.getEnumConstants() ;
        editor.getItems().setAll(values);
        editor.valueProperty().bindBidirectional(valueProperty());
    }

    @Override
    public Node getEditor() {
        return editor ;
    }

}

现在,对于值列的单元格实现,您需要一些技巧。这似乎有效:

public class ParameterValueEditingCell extends TableCell<Parameter<?>, Object> {


    @Override
    public void updateItem(Object item, boolean empty) {
        super.updateItem(item, empty);
        if (empty || item == null) {
            setText(null);
            setGraphic(null);
        } else {
            if (isEditing()) {
                setText(null);
                Parameter<?> param = getTableView().getItems().get(getIndex());
                setGraphic(param.getEditor());
            } else {
                setText(item.toString());
                setGraphic(null);
            }
        }
    }

    @Override
    public void startEdit() {
        // check if current parameter is editable and bail if not:

        int index = getIndex();
        if (index < 0 || index >= getTableView().getItems().size()) {
            return ;
        }
        if (! getTableView().getItems().get(index).isEditable()) {
            return ;
        }

        super.startEdit();
        setText(null);
        setGraphic(getTableView().getItems().get(getIndex()).getEditor());
    }

    @Override
    public void cancelEdit() {
        super.cancelEdit();
        Object item = getItem();
        setText(item == null ? null : item.toString());
        setGraphic(null);
    }

}

最后,你可以设置为

TableView<Parameter<?>> table = new TableView<>();
table.setEditable(true);
TableColumn<Parameter<?>, Object> valueColumn = new TableColumn<>("Value");

// I can't see any way to set this up without the ugly (unchecked) cast
// Any ideas?
valueColumn.setCellValueFactory(cellData -> (ObservableValue<Object>)cellData.getValue().valueProperty());

valueColumn.setCellFactory(tc -> new ParameterValueEditingCell());

我做了一个完整的例子here