如何在 JavaFX 可编辑 TreeTableView 中提供货币编辑 (LongProperty)?

How to provide Editing of a currency (LongProperty) within a JavaFX editable TreeTableView?

如何将 BO 货币 (LongProperty) 绑定到 Javafx 可编辑 TreeTableView?使用数据绑定、TextFormatter 和其他 javaFX 材料。

对于普通的 TextField,我找到了这个解决方案:

博:

import java.util.Random;

import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;

public class SimpleBo {
        //a simple LongProperty to store the currency without fractional digits (56,81 € would be 5681)
        private LongProperty currencyLong = new SimpleLongProperty();
        public SimpleBo() {
            setCurrencyLong(new Random().nextLong());
        }
        public final LongProperty currencyLongProperty() {
            return this.currencyLong;
        }
        public final long getCurrencyLong() {
            return this.currencyLongProperty().get();
        }
        public final void setCurrencyLong(final long currencyLong) {
            this.currencyLongProperty().set(currencyLong);
        }
}

申请:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.cell.TextFieldTreeTableCell;
import javafx.scene.control.cell.TreeItemPropertyValueFactory;
import javafx.stage.Stage;

public class BindingExample extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Scene scene = new Scene(createTreeTableView());
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private static TreeTableView<SimpleBo> createTreeTableView() {
        TreeTableView<SimpleBo> treeTableView = new TreeTableView<>();

        // Create column (Data type of Long).
        TreeTableColumn<SimpleBo, Number> currencyColumn = new TreeTableColumn<>("Currency");

        //Bind Values
        currencyColumn.setCellValueFactory(new TreeItemPropertyValueFactory<>("currencyLong"));

        //Set Cell Factory
        currencyColumn.setCellFactory( param -> new TextFieldTreeTableCell<>());

        // Add columns to TreeTable.
        treeTableView.getColumns().add(currencyColumn);

        SimpleBo firstBo = new SimpleBo();
        SimpleBo secondBo = new SimpleBo();
        SimpleBo thirdBo = new SimpleBo();

        // Root Item
        TreeItem<SimpleBo> itemRoot = new TreeItem<>(null);
        TreeItem<SimpleBo> itemFirst = new TreeItem<>(firstBo);
        TreeItem<SimpleBo> itemSecond = new TreeItem<>(secondBo);
        TreeItem<SimpleBo> itemThird = new TreeItem<>(thirdBo);

        itemRoot.getChildren().addAll(itemFirst, itemSecond, itemThird);

        // Set root Item for Tree
        treeTableView.setRoot(itemRoot);
        treeTableView.setShowRoot(false);
        treeTableView.setEditable(true);

        return treeTableView;
    }
}

目标应该是:

解决方案将由具有自定义设计的“CurrencyTextFieldTreeTableCell”组成。

使用 MyNumberStringConverter 和 Util Class ()

CurrencyTextFieldTreeTableCell:

import javafx.geometry.Insets;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.control.TreeTableCell;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.util.Callback;

public class CurrencyTextFieldTreeTableCell extends TreeTableCell<SimpleBo, Number> {

    public static Callback<TreeTableColumn<SimpleBo, Number>, TreeTableCell<SimpleBo, Number>> forTreeTableColumn() {
        return forTreeTableColumn(new MyNumberStringConverter());
    }

    public static Callback<TreeTableColumn<SimpleBo, Number>, TreeTableCell<SimpleBo, Number>> forTreeTableColumn(
            final MyNumberStringConverter converter) {
        return list -> new CurrencyTextFieldTreeTableCell(converter);
    }

    /***************************************************************************
     * * Fields * *
     **************************************************************************/

    private HBox hBox = new HBox();
    private Label currencyLabel = new Label("€");
    private TextField textField = new TextField("" + 0l);


    /***************************************************************************
     * * Constructors * *
     **************************************************************************/

    public CurrencyTextFieldTreeTableCell() {
        this(new MyNumberStringConverter());
    }

    private CurrencyTextFieldTreeTableCell(MyNumberStringConverter converter) {
        this.getStyleClass().add("currency-text-field-tree-table-cell");
        this.converter = converter;
        setupTextField();
        setupHBox();
        setStyle("-fx-alignment: CENTER-RIGHT;");
    }

    /***************************************************************************
     * * Properties * *
     **************************************************************************/

    private MyNumberStringConverter converter = new MyNumberStringConverter();

    /** {@inheritDoc} */
    @Override
    public void startEdit() {
        if (!isEditable() || !getTreeTableView().isEditable() || !getTableColumn().isEditable()) {
            return;
        }
        super.startEdit();

        if (isEditing()) {
            this.setText(null);

            if (hBox != null) {
                this.setGraphic(hBox);
            } else {
                this.setGraphic(textField);
            }
            if (textField != null) {
                textField.setText(getItemText());
                textField.selectAll();
                // requesting focus so that key input can immediately go into the
                // TextField (see RT-28132)
                textField.requestFocus();
            }
        }
    }

    /** {@inheritDoc} */
    @Override
    public void cancelEdit() {
        super.cancelEdit();
        this.setText(getItemText());
        this.setGraphic(currencyLabel);
        contentDisplayProperty().setValue(ContentDisplay.RIGHT);
    }

    /** {@inheritDoc} */
    @Override
    public void updateItem(Number item, boolean empty) {
        super.updateItem(item, empty);
        if (isEmpty()) {
            setText(null);
            setGraphic(null);
        } else {
            if (isEditing()) {
                if (textField != null) {
                    textField.setText(getItemText());
                }
                setText(null);
                setGraphic(hBox);
            } else {
                setText(getItemText());
                setGraphic(currencyLabel);
                contentDisplayProperty().setValue(ContentDisplay.RIGHT);
            }
        }
    }

    private void setupTextField() {
        TextFormatter<Number> textFormatter = new TextFormatter<>(Util.createFilter());
        this.textField.setTextFormatter(textFormatter);
        // Use onAction here rather than onKeyReleased (with check for Enter),
        // as otherwise we encounter RT-34685
        this.textField.setOnAction(event -> {
            if (converter == null) {
                throw new IllegalStateException("Attempting to convert text input into Object, but provided "
                        + "StringConverter is null. Be sure to set a StringConverter " + "in your cell factory.");
            }
            commitEdit(converter.fromString(textField.getText()).longValue());
            event.consume();
        });
        this.textField.setOnKeyReleased(t -> {
            if (t.getCode() == KeyCode.ESCAPE) {
                cancelEdit();
                t.consume();
            }
        });
    }

    private void setupHBox() {
        this.hBox.getChildren().add(this.textField);
        this.hBox.getChildren().add(new Label(" €"));
        this.hBox.setPadding(new Insets(this.hBox.getPadding().getTop() + 9.0D, this.hBox.getPadding().getRight(), this.hBox.getPadding().getBottom(), this.hBox.getPadding().getLeft()));
    }

    private String getItemText() {
        if(converter == null) {
            return getItem() == null ? "" : getItem().toString();
        } else {
            return converter.toString(getItem());
        }
    }
}

并将 setCellFactory 更改为:

    NumberFormat nFormat = NumberFormat.getInstance();
    nFormat.setMinimumIntegerDigits(1);
    nFormat.setMinimumFractionDigits(2);
    nFormat.setMaximumFractionDigits(2);
    MyNumberStringConverter numberStringConverter = new MyNumberStringConverter(nFormat);
    // Set Cell Factory
    currencyColumn.setCellFactory(
            param -> CurrencyTextFieldTreeTableCell.forTreeTableColumn(numberStringConverter).call(param));