JavaFX TableView可编辑单元格+编辑后更改样式

JavaFX TableView editable cell + change style after edit

我需要使 TableView 中的单元格可编辑,并在更改提交后将其设为粗体。我可以单独制作这些东西,但不能一起制作,因为它们是通过 setCellFactory 方法实现的。

以下是代码部分:

设置细胞工厂

    ((TableColumn) lpAttributesTable.getColumns().get(1)).setCellFactory(
                new Callback<TableColumn<LPAttributesTableObject,String>, TableCell<LPAttributesTableObject,String>>() {
    @Override
    public TableCell<LPAttributesTableObject,String> call(
        TableColumn<LPAttributesTableObject,String> param) {
        TextFieldTableCell<LPAttributesTableObject, String> editableTableCell = 
            new TextFieldTableCell<LPAttributesTableObject, String>(new StringConverter<String>() {
            @Override
            public String toString(String object) {
                return object;
            }

            @Override
            public String fromString(String string) {
                return string;
            }
        })
        {
            @Override
            public void updateItem(String item, boolean empty)
            {
                super.updateItem(item,empty);

                if (item == null || empty) {
                    setText(null);
//                        setStyle("");
                }
                else
                {
                    setText(item);
                    styleProperty().bind(
                    Bindings.when(getTableRow().selectedProperty()).
                        then("-f-font-weight:bold;").otherwise(""));

                }
            }
        };
        return editableTableCell;
    }
});

LPAttributesTable对象

public class LPAttributesTableObject {
    private String attribute;
    private String value;

    public LPAttributesTableObject(String _attribute, String _value)
    {
        this.attribute = _attribute;
        this.value = _value;
    }

    public final String getAttribute() { return attribute; }
    public final String getValue() { return value; }
    public StringProperty attributeProperty() { return new SimpleStringProperty(attribute); }
    public StringProperty valueProperty() { return new SimpleStringProperty(value); }
    public final void setAttribute(String _attr) { this.attribute = _attr;}
    public final void setValue(String _description) { this.value = _description;}
}

Table 与模型绑定。请注意,我已经注释掉了仅编辑 cellFactory

((TableColumn) lpattrsTable.getColumns().get(1)).setCellValueFactory(new PropertyValueFactory<LPAttributesTableObject,String>("value"));
//        ((TableColumn) lpattrsTable.getColumns().get(1)).setCellFactory(TextFieldTableCell.forTableColumn(new DefaultStringConverter()));

当前实现仅允许单元格编辑。

请注意,仅绑定到选定的 属性 是行不通的。您还需要保存该项目已被以某种方式编辑的事实。这可以使用项目的 属性 或 ObservableMap.

来完成

此外,您还需要实际设置该项目已被编辑的信息。这可以通过覆盖 commitEdit 方法来完成。

// external storage of edited states by index
ObservableMap<Number, Boolean> edited = FXCollections.observableHashMap();

TableColumn<LPAttributesTableObject, String> column = (TableColumn) lpAttributesTable.getColumns().get(1);

StringConverter<String> converter = new DefaultStringConverter();

column.setCellFactory(t -> new TextFieldTableCell<LPAttributesTableObject, String>(converter) {
    
    private final BooleanBinding editedBinding = Bindings.booleanValueAt(edited, indexProperty());
    
    {
        editedBinding.addListener((a, b, newValue) -> setStyle(newValue ? "-fx-font-weight:bold;" : ""));
    }

    @Override
    public void commitEdit(String newValue) {
        if (!Objects.equals(newValue, getItem())) {
            // save data that this index has been edited
            edited.put(getIndex(), Boolean.TRUE);
        }
        super.commitEdit(newValue);
    }

});

请注意,如果 table 的项目列表被修改,您可能需要修改地图。


备选

使用存储项目是否被编辑的值类型而不是简单的 String 允许您保持代码简单,即使项目列表被修改:

public class EditableString {

    private final boolean edited;
    private final String value;

    public EditableString(boolean edited, String value) {
        this.edited = edited;
        this.value = value;
    }

    public EditableString(String value) {
        this(false, value);
    }

    public boolean isEdited() {
        return edited;
    }

    public String getValue() {
        return value;
    }

}
StringConverter<EditableString> converter = new StringConverter<EditableString>() {

    @Override
    public String toString(EditableString object) {
        return object == null ? null : object.getValue();
    }

    @Override
    public EditableString fromString(String string) {
        return string == null ? null : new EditableString(true, string);
    }

};

TableColumn<LPAttributesTableObject, EditableString> column = (TableColumn) lpAttributesTable.getColumns().get(1);

column.setCellFactory(t -> new TextFieldTableCell<LPAttributesTableObject, EditableString>(converter) {

    @Override
    public void updateItem(EditableString item, boolean empty) {
        super.updateItem(item, empty);

        if (item != null && item.isEdited()) {
            setStyle("-fx-font-weight: bold;");
        } else {
            setStyle("");
        }
    }

    @Override
    public void commitEdit(EditableString newValue) {
        EditableString currentItem = getItem();
        if (currentItem == null || newValue == null || !Objects.equals(newValue.getValue(), currentItem.getValue())) {
            super.commitEdit(newValue);
        } else {
            // if the string is the same, keep the old value
            super.commitEdit(currentItem);
        }
    }

});

请注意,这需要您将包含 StringLPAttributesTableObject 的 属性 更改为包含 EditableString.[=19= 的 属性 ]

最后这就是我的意思。

    StringConverter<EditableString> converter = new StringConverter<EditableString>() {
                @Override
                public String toString(EditableString object) {
                    System.out.println("bla bla");
                    return object == null ? null : object.toString();
                }

                @Override
                public EditableString fromString(String string) {
                    System.out.println("bla");
                    return string == null ? null : new EditableString(true, string);
                }
            };


((TableColumn) lpAttributesTable.getColumns().get(1)).setCellValueFactory(new PropertyValueFactory<LPAttributesTableObject,EditableString>("val"));
            ((TableColumn) lpAttributesTable.getColumns().get(0)).setCellValueFactory(new PropertyValueFactory<LPAttributesTableObject,String>("attribute"));
    TableColumn<LPAttributesTableObject, EditableString> col = (TableColumn) lpAttributesTable.getColumns().get(1);
            col.setCellFactory(TextFieldTableCellEditable.<LPAttributesTableObject, EditableString>forTableColumn(converter));

我必须将现有的 TextFieldTableCell 对象扩展为:

public class TextFieldTableCellEditable<S,EditableString> extends TextFieldTableCell<S,EditableString> {

    public static <S,EditableString> Callback<TableColumn<S,EditableString>, TableCell<S,EditableString>> forTableColumn(
            final StringConverter<EditableString> converter) {
        return list -> new TextFieldTableCellEditable<S, EditableString>(converter);
    }

    public TextFieldTableCellEditable(StringConverter<EditableString> converter) {
        this.getStyleClass().add("text-field-table-cell");
        setConverter(converter);
    }


    @Override
    public final void updateItem(EditableString item, boolean empty) {

        super.updateItem(item, empty);
        if (item != null && ((sample.EditableString)item).isEdited()) {
            setText(item.toString());
            setStyle("-fx-font-weight: bold;");
        } else if (item != null && !((sample.EditableString)item).isEdited()){
            setStyle("");
            setText(item.toString());
        } else {
            setStyle("");
            setText(null);
        }
    }

    @Override
    public void commitEdit(EditableString newValue) {
        EditableString currentItem = getItem();
        if (currentItem == null || newValue == null || !Objects.equals(((sample.EditableString)newValue).getValue(), ((sample.EditableString)currentItem).getValue())) {
            super.commitEdit(newValue);
        } else {
            // if the string is the same, keep the old value
            super.commitEdit(currentItem);
        }
    }

}

LPAttributesTableObject 如下所示:

public class LPAttributesTableObject {
    private String attribute;
    private EditableString val;

    public LPAttributesTableObject(String _attribute, EditableString _value)
    {
        this.attribute = _attribute;
        this.val = _value;
    }

    public String getAttribute() { return attribute.toString(); }
    public EditableString getVal() { return val; }
    public final EditableString getValue() { return val; }
    public StringProperty attributeProperty() { return new SimpleStringProperty(attribute.toString()); }
    public ObjectProperty<EditableString> valProperty() { return new SimpleObjectProperty<EditableString>(this,"value",val); }
    public void setAttribute(String _attr) { this.attribute = _attr;}
    public void setValue(EditableString _value) { this.val = _value;}
    public void setVal(EditableString _val) { this.val = _val; }
    public void setVal(String _val) { this.val.setValue(_val);}


}

EditableString 看起来像这样:

public class EditableString { 
    private boolean edited;
    private String string;

    public EditableString(boolean edited, String val)
    {
        this.edited = edited;
        this.string = val;
    }

    public EditableString(String val)
    {
        this.edited = false;
        this.string = val;
    }

    public String getValue() { return string; }

    public void setValue(String val) { this.string = val; }

    public String toString() { return string; }

    public boolean isEdited() { return edited; }

    public void setValue(EditableString estr) { this.setValue(estr.getValue()); this.edited = estr.edited; }
}