Двунаправленная привязка с ObjectBinding в JavaFX
У меня есть простой 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;
}
}
Как вы могли видеть, я пытаюсь рассчитать процент, когда значение установлено, и вычислить значение, когда ставка установлена. Связывание, которое я пробовал выше, не может быть связано, так как это войдет в вечный цикл. Есть ли способ, которым я могу сделать привязку для решения этой проблемы, или мне нужно сделать расчет внутри сеттеров?
Ответы
Ответ 1
Вам нужно будет прослушать изменения в полях, но следить за тем, чтобы слушатель был уволен, чтобы не запускать снова в бесконечных циклах. Вдохновение было фактическим кодом из JavaFX, декомпилированным здесь.
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() + "%");
}