如何将带有 TextFormatter 和 Listener 的 TextField 添加到 TableCell 中?

How to add TextField with TextFormatter and Listener into TableCell?

我有 Water 个带有 Integer 参数 value 的对象。如果尚未创建,则此参数可以为空,等于 null。值将存储为 Integer,但在呈现时,转换为 Decimal 并为用户打印为 String 并应用 TextFormatter。用户进行更改后,应将其转换回适当的 Integer 并存储在数据库中。

我的Water对象:

private Integer value; 

table的代码:

@FXML
private TableView<Water> waterTable;

@FXML
private TableColumn<Water, Integer> valueColumn;

@FXML
private void initialize(){
    waterTable.setItems(FXCollections.observableArrayList(thisMonthWaterList));

    //don't know how to implemeny here instead of this code cell with TextField
    /*valueColumn.setCellValueFactory(cellData -> new 
    SimpleIntegerProperty(cellData.getValue().getAmount()).asObject());
        valueColumn.setCellFactory(new Callback<TableColumn<Water,Integer>, 
    TableCell<Water,Integer>>() {

            @Override
            public TableCell<Water, Integer> call(TableColumn<Water, Integer> param) {
                return new TableCell<Water, Integer>(){
                    @Override
                    protected void updateItem(Integer value, boolean empty){
                        super.updateItem(value, empty);
                        if(value==null || empty){
                            setText(null);
                        }else{
                            setText(value.toString());
                        }
                    }
                };
            }
        });*/
}

这是 TextFormatter 的代码:

TextField.setTextFormatter(new TextFormatter<>(change -> {
            int maxLength = 10;

            if (change.isAdded()) {
                if(change.getControlNewText().length()<=maxLength){
                    if (change.getText().contains(",")) {
                        change.setText(change.getText().replaceAll(",", "."));
                    }
                    change = change.getControlNewText().matches("^\d*(\.\d{0,1})?$") ? change : null;
                }else{
                    if(change.getText().length()==1){
                        change = null;
                    }else{
                        int allowedLength = maxLength - change.getControlText().length();
                        change.setText(change.getText().substring(0, allowedLength));
                    }
                }
            }
            return change;
        }));

Listener的代码:

TextField.focusedProperty().addListener(new FocusChangeListener(TextField, text -> {
            if(text.isEmpty()){
                TextField.setText(Water.getAreaFormat());
            }else{
                TextField.setText(Water.toString(TextField.getText()));
            }
            thisObject.setValue(Water.toInt(TextField.getText()));
            WaterDA.update(thisObject);
        }, text -> {
            if(text.equals(Water.getWaterFormat())){
                TextField.setText("");
            }
        }));

实际上,您可以使用 TextFormatter 使用 StringConverterUnaryOperator 来处理转换和防止无效输入。以下代码假定您在 Water class 和 amountProperty() 方法 returns 中有一个 ObjectProperty<Integer>

如果Water.toString(int)Water.toString(Integer)方法不存在,您需要实现从int到字符串的转换才能使下面的代码生效。

private static final StringConverter<Integer> VALUE_CONVERTER = new StringConverter<Integer>() {

    @Override
    public String toString(Integer object) {
        return object == null ? Water.getAreaFormat() : Water.toString(object);
    }

    @Override
    public Integer fromString(String string) {
        return Water.toInt(string);
    }

};

// filter copied unmodified from your code
private static final UnaryOperator<TextFormatter.Change> VALUE_FILTER = change -> {
    int maxLength = 10;

    if (change.isAdded()) {
        if(change.getControlNewText().length() <= maxLength){
            if (change.getText().contains(",")) {
                change.setText(change.getText().replaceAll(",", "."));
            }
            change = change.getControlNewText().matches("^\d*(\.\d{0,1})?$") ? change : null;
        } else {
            if (change.getText().length() == 1){
                change = null;
            } else {
                int allowedLength = maxLength - change.getControlText().length();
                change.setText(change.getText().substring(0, allowedLength));
            }
        }
    }
    return change;
};

@FXML
private TableColumn<Water, Integer> valueColumn;

@FXML
private void initialize(){
    waterTable.setItems(FXCollections.observableArrayList(thisMonthWaterList));

    valueColumn.setCellValueFactory(cellData -> cellData.getValue().amountProperty());
    valueColumn.setCellFactory(new Callback<TableColumn<Water, Integer>, TableCell<Water, Integer>>() {

        @Override
        public TableCell<Water, Integer> call(TableColumn<Water, Integer> param) {
            return new TableCell<Water, Integer>() {

                private final TextFormatter<Integer> formatter;
                private final TextField textField;

                {
                    textField = new TextField();
                    formatter = new TextFormatter<>(VALUE_CONVERTER, null, VALUE_FILTER);
                    textField.setTextFormatter(formatter);
                    formatter.valueProperty().addListener((o, oldValue, newValue) -> {
                        Water water = (Water) getTableRow().getItem();
                        if (!Objects.equals(water.getAmount(), newValue)) {
                             // update item and db, if value was modified
                             water.setAmount(newValue);
                             WaterDA.update(water);
                        }
                    });
                }

                @Override
                protected void updateItem(Integer value, boolean empty){
                    super.updateItem(value, empty);
                    if (empty){
                        setGraphic(null);
                    } else {
                        setGraphic(textField);
                        formatter.setValue(value);
                    }
                }
            };
        }
    });

}

这假设您的 TableCell 应该始终在 "editing state" 中。如果不是这种情况,则需要在 startEdit/cancelEditcommitEdit 方法中实现状态更改。