Поскольку текстовое поле, представляющее итог, не редактируется, вам не нужна здесь двунаправленная привязка, просто обычная привязка, которая обновляет одно значение (значение итога) при изменении вычисленного значения.
Вы может делать что-то вроде
total.valueProperty().bind(Bindings.createObjectBinding(
() -> quantity.getValue() * price.getValue(),
quantity.valueProperty(), price.valueProperty()));
Методы Bindings.createXXXBinding(...)
создают наблюдаемые значения, значения которых вычисляются (здесь по существу по формуле quantity * price
). Привязка выражает тот факт, что при изменении вычисленного значения значение total
должно измениться. Двунаправленная привязка выражает идею о том, что при изменении одного из двух значений другое должно быть установлено в соответствии с ним. Это просто не сработает в этом сценарии, потому что вы не можете установить вычисленное значение.
С точки зрения программирования это работает, потому что метод bindBidirectional()
ожидает WritableValue
(соответствующего типа) ; методы Bindings.createXXXBinding()
возвращают ObservableValue
, которое не является WritableValue
. (Напротив, метод bind()
ожидает только ObservableValue
.) Это имеет смысл также с точки зрения semanti c: если вы знаете общую сумму, не существует единственного способа определить цену и количество; однако, если вы знаете цену и количество, вы можете определить общую сумму. Так что отношения не симметричны c.
Вот полный рабочий пример. Вероятно, это можно существенно улучшить, например, используя TextFormatter
s, которые ограничивают допустимую запись, и пользовательские реализации StringConverter
, которые используют локализованные числовые форматы, и т. Д. c.
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();
}
}