我有一个简单的bean,它有一些相互关联的属性。例如,此bean具有名为 discountRate 的属性,另一个名为 discountValue 。 discountRate是应用于销售的折扣百分比(%)。 discountValue是应用于销售的折扣价值($)。由于用户可以通知百分比或值,并且我需要在数据库中存储这两个值,因此JavaFX双向绑定可以解决问题,但是,正如您可以想象的那样,这些值是相关的但不相同。我尝试解决这个问题,在双方创建绑定:
public class ExampleBean{
private ObjectProperty<BigDecimal> discountValue;
private ObjectProperty<BigDecimal> discountRate;
public BigDecimal getDiscountvalue() {
return discountValueProperty().getValue();
}
public void setDiscountValue(BigDecimal discountvalue) {
this.discountValueProperty().set(discountvalue);
}
public ObjectProperty<BigDecimal> discountValueProperty() {
if(discountValue==null){
discountValue=new SimpleObjectProperty<BigDecimal>(new BigDecimal("0.00"));
discountRate=new SimpleObjectProperty<BigDecimal>(new BigDecimal("0.00"));
configureDiscountBinding();
}
return discountValue;
}
private void configureDiscountBinding(){
discountValue.bind(Bindings.createObjectBinding(new Callable<BigDecimal>() {
@Override
public BigDecimal call() throws Exception {
return getDiscountRate().multiply(getTotalValue()).divide(new BigDecimal("100"));
}
}, discountRateProperty()));
discountRate.bind(Bindings.createObjectBinding(new Callable<BigDecimal>() {
@Override
public BigDecimal call() throws Exception {
return getDiscountValue().multiply(new BigDecimal("100")).divide(getTotalValue());
}
}, discountValueProperty()));
}
public BigDecimal getDiscountRate() {
return discountRateProperty().getValue();
}
public void setDiscountRate(BigDecimal discountRate) {
this.discountRateProperty().set(discountRate);
}
public ObjectProperty<BigDecimal> discountRateProperty() {
if(discountRate==null){
discountRate=new SimpleObjectProperty<BigDecimal>(new BigDecimal("0.00"));
discountValue=new SimpleObjectProperty<BigDecimal>(new BigDecimal("0.00"));
configureDiscountBinding();
}
return discountRate;
}
}
正如您所看到的,我正在尝试计算设置值时的百分比,并在设置速率时计算值。我在上面尝试的绑定不能被绑定,因为这将进入一个永恒的循环。有没有办法可以解决这个问题,或者我需要在setter中进行计算?
答案 0 :(得分:10)
您需要收听字段的更改,但要跟踪是否已触发侦听器,以免在无限循环中再次触发。灵感来自JavaFX的实际代码,反编译here。
private void configureDiscountBinding() {
discountValue.addListener(new ChangeListener<BigDecimal>() {
private boolean changing;
@Override public void changed(ObservableValue<? extends BigDecimal> observable, BigDecimal oldValue, BigDecimal newValue) {
if( !changing ) {
try {
changing = true;
discountRate.set(newValue.multiply(new BigDecimal("100")).divide(getTotalValue(), RoundingMode.HALF_DOWN));
}
finally {
changing = false;
}
}
}
});
discountRate.addListener(new ChangeListener<BigDecimal>() {
private boolean changing;
@Override public void changed(ObservableValue<? extends BigDecimal> observable, BigDecimal oldValue, BigDecimal newValue) {
if( !changing ) {
try {
changing = true;
discountValue.set(newValue.multiply(getTotalValue()).divide(new BigDecimal("100"), RoundingMode.HALF_DOWN));
}
finally {
changing = false;
}
}
}
});
}
这简单而繁琐;如果您正在广泛使用此功能,则可以将内部ChangeListener
重构为某种常见类型或其他一些聪明的解决方案。
我使用以下主要测试了上面的代码(您必须提供BigDecimal getTotalValue()
方法,在我的情况下,我只返回一个常量BigDecimal
):
public static void main(String[] args) {
ExampleBean e = new ExampleBean();
System.out.println("Setting rate to 50%");
e.discountRateProperty().set(new BigDecimal(50.0));
System.out.println("-> value=" + e.getDiscountvalue());
System.out.println("Setting value to 25");
e.discountValueProperty().set(new BigDecimal(25.0));
System.out.println("-> rate=" + e.getDiscountRate() + "%");
}