在以下代码中计算文本字段的整数和双精度值之间的数字时如何应用双向方法?

how to apply bidirectional method when doing calculation number between integer and double value of text field in the following code?

我不熟悉绑定,尤其是双向绑定。我有数量、价格和总计三个文本字段。以下在线代码将这些文本字段的值转换为 Integer 和 Double 格式。

    StringConverter<Number> converter = new NumberStringConverter();
    StringConverter<Double> toDouble = new DoubleStringConverter();
    
    TextFormatter<Number> quantity = new TextFormatter<>(converter);
    TextFormatter<Double> price = new TextFormatter<>(toDouble);
    TextFormatter<Double> total = new TextFormatter<>(toDouble);

    Quantity.setTextFormatter(quantity);
    Price.setTextFormatter(price);
    Total.setTextFormatter(total);
    
    total.valueProperty().bindBidirectional(Bindings.createObjectBinding(() -> {
        
        // ?????????????????????????????????????????
        
    }, quantity.valueProperty(), price.valueProperty()));
    

每当我在文本字段中输入不同的数字时,请帮助填写计算或任何其他方式来触发更改?

由于表示总计的文本字段不可编辑,因此您不需要在此处进行双向绑定,只需一个在计算值更改时更新一个值(总计值)的常规绑定。

你可以这样做

total.valueProperty().bind(Bindings.createObjectBinding(
    () -> quantity.getValue() * price.getValue(),
    quantity.valueProperty(), price.valueProperty()));

Bindings.createXXXBinding(...) 方法创建可观察值,其值是计算出来的(这里基本上是通过公式 quantity * price)。绑定表达了这样一个事实,即当计算值发生变化时,total 的值也应该发生变化。双向绑定表达的想法是,当两个值中的任何一个发生变化时,另一个值都应该设置为匹配它。这在这种情况下根本行不通,因为您无法设置计算值。

从编程的角度来看,这是有效的,因为 bindBidirectional() 方法需要一个 WritableValue(适当的类型); Bindings.createXXXBinding() 方法 return 一个 ObservableValue,它不是 WritableValue。 (相比之下,bind() 方法只需要一个 ObservableValue。)这从语义的角度来看也是有意义的:如果你知道总数,就没有唯一的方法来确定价格和数量;但是,如果您知道价格和数量,则可以确定总数。所以关系不是对称的。

这是一个完整的工作示例。这可能会得到显着改善,例如通过使用限制有效输入的 TextFormatter 和使用本地化数字格式的自定义 StringConverter 实现等

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.util.converter.DoubleStringConverter;
import javafx.util.converter.IntegerStringConverter;


public class App extends Application {

    @Override
    public void start(Stage stage) {
        
        TextFormatter<Integer> quantity = new TextFormatter<>(new IntegerStringConverter(), 0);
        TextFormatter<Double> price = new TextFormatter<>(new DoubleStringConverter(), 0.0);
        TextFormatter<Double> total = new TextFormatter<>(new DoubleStringConverter(), 0.0);
        
        
        
        total.valueProperty().bind(Bindings.createObjectBinding(
                () -> quantity.getValue() * price.getValue(), 
                quantity.valueProperty(), price.valueProperty()));
        
        TextField quantityTF = new TextField("0");
        quantityTF.setTextFormatter(quantity);
        
        TextField priceTF = new TextField("0.0");
        priceTF.setTextFormatter(price);
        TextField totalTF = new TextField();
        totalTF.setEditable(false);
        totalTF.setTextFormatter(total);
        
        
        GridPane root = new GridPane();
        ColumnConstraints leftCol = new ColumnConstraints();
        leftCol.setHalignment(HPos.RIGHT);
        ColumnConstraints rightCol = new ColumnConstraints();
        rightCol.setHalignment(HPos.LEFT);
        root.getColumnConstraints().setAll(
                leftCol, rightCol
        );
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(5));
        root.setHgap(5);
        root.setVgap(5);
        root.addRow(0, new Label("Price:"), priceTF);
        root.addRow(1, new Label("Quantity:"), quantityTF);
        root.addRow(2, new Label("Total:"), totalTF);
        
        stage.setScene(new Scene(root, 800, 500));
        stage.show();
    }

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

}