JavaFX中与ObjectBinding的双向绑定

我有一个简单的bean,它有一些相互关联的属性。 例如,这个bean有一个叫做discountRate的属性,另一个叫做discountValue 。 discountRate是应用于销售的折扣百分比(%)。 discountValue是应用于销售的折扣价值($)。 由于用户可以通知百分比或值,并且我需要在数据库中存储这两个值,因此JavaFX双向绑定可以解决问题,但是,正如您可以想象的那样,这些值是相关的但不相同。 我尝试解决这个问题,在双方创建绑定:

public class ExampleBean{ private ObjectProperty discountValue; private ObjectProperty discountRate; public BigDecimal getDiscountvalue() { return discountValueProperty().getValue(); } public void setDiscountValue(BigDecimal discountvalue) { this.discountValueProperty().set(discountvalue); } public ObjectProperty discountValueProperty() { if(discountValue==null){ discountValue=new SimpleObjectProperty(new BigDecimal("0.00")); discountRate=new SimpleObjectProperty(new BigDecimal("0.00")); configureDiscountBinding(); } return discountValue; } private void configureDiscountBinding(){ discountValue.bind(Bindings.createObjectBinding(new Callable() { @Override public BigDecimal call() throws Exception { return getDiscountRate().multiply(getTotalValue()).divide(new BigDecimal("100")); } }, discountRateProperty())); discountRate.bind(Bindings.createObjectBinding(new Callable() { @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 discountRateProperty() { if(discountRate==null){ discountRate=new SimpleObjectProperty(new BigDecimal("0.00")); discountValue=new SimpleObjectProperty(new BigDecimal("0.00")); configureDiscountBinding(); } return discountRate; } } 

正如您所看到的,我正在尝试计算设置值时的百分比,并在设置速率时计算该值。 我上面尝试的绑定不能被绑定,因为这将进入一个永恒的循环。 有没有办法可以做一个绑定来解决这个问题,或者我需要在setter中进行计算?

您需要监听对字段的更改,但要跟踪是否已触发侦听器,以免在无限循环中再次触发。 灵感来自JavaFX的实际代码, 在这里反编译。

 private void configureDiscountBinding() { discountValue.addListener(new ChangeListener() { private boolean changing; @Override public void changed(ObservableValue 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() { private boolean changing; @Override public void changed(ObservableValue 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; } } } }); } 

这简单而繁琐; 如果您正在广泛使用此function,则可以将内部ChangeListener重构为某种常见类型或其他一些聪明的解决方案。

我用以下main测试了上面的代码(你必须提供一个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() + "%"); }