JavaFX中的自定义双向绑定 [英] Custom bidirectional bindings in JavaFX
问题描述
我正在尝试实现一个涉及2个字段的计算的GUI.我的模型有2个属性和1个绑定.
I'm trying to implements a GUI involving a computation of 2 fields. My model has 2 properties and one binding.
ObjectProperty<BigDecimal> price = new SimpleObjectProperty<>();
ObjectProperty<BigDecimal> quantity= new SimpleObjectProperty<>();
ObjectBinding<BigDecimal> totalPrice = new ObjectBinding<BigDecimal>() {
{ bind(price,quantity);}
protected BigDecimal computeValue() {
if (price.get() == null || quantity.get() == null) return null;
return price.get().multiply(quantity.get());
}
};
我的GUI有3个 TextField
以匹配价格,金额和totalPrice.通常,我正在属性与我的 TextField
之类的常规绑定
My GUI has 3 TextField
to match price, amount, totalPrice. Usualy, I'm doing a regular binding between my properties and my TextField
as such
priceTextField.textProperty().bindBidirectional(myModel.priceProperty(), new NumberStringConverter());
现在这有点棘手.如果用户修改价格或数量,则必须更新TotalPrice(这是绑定到目前为止所做的事情).但我希望能够执行以下操作:如果用户更新TotalPrice,则它将相应地将数量重新计算为固定价格.
Now this where it gets a bit tricky. If the user modify the price or the quantity, it has toupdate the TotalPrice (which is what the binding does so far). But I want to be able to do the following: if the user update the TotalPrice, then it recomputes the quantity accordingly to the fixed price.
所以问题是:如何创建这样的流=> TotalPrice绑定价格和数量,但是数量绑定TotalPrice和价格.当我在 totalPriceTextfield
中输入内容时,应该更新 quantityTextField
,反之亦然.
So the question is: how do I create such a flow => TotalPrice is bind on price and quantity, but quantity is bind on TotalPrice and price. When I input something in the totalPriceTextfield
it should update the quantityTextField
and vice versa.
谢谢.
******编辑********这是一段丑陋的代码,只是为了说明我想要实现的目标(nb:我知道我可以使用 Binding.multiply
和其他方法,但我需要将来的项目来实现计算功能)>
****** Edit ********
Here is an ugly piece of code just to illustrate what I want to achieve (nb: i know i could use Binding.multiply
and other method but I need for future project to implement the compute function)
public class TestOnBindings {
private DoubleProperty price = new SimpleDoubleProperty(10.0);
private DoubleProperty quantity = new SimpleDoubleProperty(1.0);
private DoubleProperty total = new SimpleDoubleProperty(1.0);
private DoubleBinding totalBinding = new DoubleBinding() {
{bind(quantity,price);}
@Override
protected double computeValue() {
return quantity.get()*price.get();
}
};
private DoubleBinding quantityBinding = new DoubleBinding() {
{bind(total,price);}
@Override
protected double computeValue() {
return total.get()/price.get();
}
};
public TestOnBindings(){
total.bind(totalBinding); //should really not do that, looks ugly
quantity.bind(quantityBinding); //now you're asking for troubles
}
public void setPrice(Double price){
this.price.set(price);
}
public void setQuantity(Double quantity){
this.quantity.set(quantity);
}
public void setTotal(Double total){
this.total.set(total);
}
public Double getTotal(){
return total.get();
}
public Double getQuantity(){
return quantity.get();
}
public static void main(String[] args) {
TestOnBindings test = new TestOnBindings();
test.setQuantity(5.0);
System.out.println("Total amount = " + test.getTotal());
}
}
和明显的错误:
线程"main"中的异常java.lang.RuntimeException:无法设置绑定值.在javafx.beans.property.DoublePropertyBase.set(DoublePropertyBase.java:142)
Exception in thread "main" java.lang.RuntimeException: A bound value cannot be set. at javafx.beans.property.DoublePropertyBase.set(DoublePropertyBase.java:142)
推荐答案
我认为我的问题已经在另一个博客
I think that my question has already been answered in another post and that this guy provided a simple way on his blog
我对他的课程进行了一些修改,以适应我的需要,并开设了一个非常易于使用的课程:
I modified a bit his class to adapt my needs and have a very easy to use class:
public class CustomBinding {
public static <A ,B> void bindBidirectional(Property<A> propertyA, Property<B> propertyB, Function<A,B> updateB, Function<B,A> updateA){
addFlaggedChangeListener(propertyA, propertyB, updateB);
addFlaggedChangeListener(propertyB, propertyA, updateA);
}
public static <A ,B> void bind(Property<A> propertyA, Property<B> propertyB, Function<A,B> updateB){
addFlaggedChangeListener(propertyA, propertyB, updateB);
}
private static <X,Y> void addFlaggedChangeListener(ObservableValue<X> propertyX, WritableValue<Y> propertyY, Function<X,Y> updateY){
propertyX.addListener(new ChangeListener<X>() {
private boolean alreadyCalled = false;
@Override
public void changed(ObservableValue<? extends X> observable, X oldValue, X newValue) {
if(alreadyCalled) return;
try {
alreadyCalled = true;
propertyY.setValue(updateY.apply(newValue));
}
finally {alreadyCalled = false; }
}
});
}
}
然后将其应用到我的示例中....仍然需要进行一些微调,但这确实可以完成工作.
and then applying it to my example.... Still need a little bit of fine tuning, but it does the job.
public class TestOnBindings {
private DoubleProperty price = new SimpleDoubleProperty(10.0);
private DoubleProperty quantity = new SimpleDoubleProperty(1.0);
private DoubleProperty total = new SimpleDoubleProperty(1.0);
public TestOnBindings(){
CustomBinding.<Number,Number>bindBidirectional(quantity, total,
(newQuantity)-> newQuantity.doubleValue() * price.get(),
(newTotal)-> newTotal.doubleValue() /price.get());
CustomBinding.<Number,Number>bind(price, total,
(newPrice)-> newPrice.doubleValue() * quantity.get());
}
public void setPrice(Double price){this.price.set(price);}
public void setQuantity(Double quantity){this.quantity.set(quantity);}
public void setTotal(Double total){this.total.set(total);}
public Double getTotal(){return total.get();}
public Double getQuantity(){return quantity.get();}
public Double getPrice(){return price.get();}
public static void main(String[] args) {
TestOnBindings test = new TestOnBindings();
test.setQuantity(5.0);
System.out.println("Quantity = " + test.getQuantity());
System.out.println("Price = " + test.getPrice());
System.out.println("Total = " + test.getTotal());
test.setTotal(60.0);
System.out.println("---------------------------------------------");
System.out.println("Quantity = " + test.getQuantity());
System.out.println("Price = " + test.getPrice());
System.out.println("Total = " + test.getTotal());
test.setPrice(5.0);
System.out.println("---------------------------------------------");
System.out.println("Quantity = " + test.getQuantity());
System.out.println("Price = " + test.getPrice());
System.out.println("Total = " + test.getTotal());
}
}
这篇关于JavaFX中的自定义双向绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!