简体   繁体   English

JavaFX中与ObjectBinding的双向绑定

[英]Bidirectional binding with ObjectBinding in JavaFX

I have a simple bean that has some properties related with each other. 我有一个简单的bean,它有一些相互关联的属性。 For example, this bean has a property called discountRate and another called discountValue . 例如,这个bean有一个叫做discountRate的属性,另一个叫做discountValue The discountRate is the percentage (%) of discount applied to a sale. discountRate是应用于销售的折扣百分比(%)。 The discountValue is the value($) of discount applied to a sale. discountValue是应用于销售的折扣价值($)。 As the user can inform either the percentage or the value and I need store the two values in the database, a JavaFX bidirectional binding would solve the problem, however, as you can imagine, these values are correlated but aren't the same. 由于用户可以通知百分比或值,并且我需要在数据库中存储这两个值,因此JavaFX双向绑定可以解决问题,但是,正如您可以想象的那样,这些值是相关的但不相同。 I tried solve this problem creating bindings in the two sides: 我尝试解决这个问题,在双方创建绑定:

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;
    }
}

As you could see, I'm trying calculate the percentage when the value is setted, and calculate the value when the rate is setted. 正如您所看到的,我正在尝试计算设置值时的百分比,并在设置速率时计算值。 The binding I tried above can't be bound, as this will enter in a eternal loop. 我在上面尝试的绑定不能被绑定,因为这将进入一个永恒的循环。 Is there a way I can do a binding to solve this problem, or I need do the calculation inside setters? 有没有办法可以做一个绑定来解决这个问题,或者我需要在setter中进行计算?

You will need to listen to changes to the fields but keep track if the listener has been fired, so as not to fire again in endless loops. 您需要监听对字段的更改,但要跟踪是否已触发侦听器,以免在无限循环中再次触发。 Inspiration was the actual code from JavaFX, decompiled here . 灵感来自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;
                }
            }
        }
    });
}

This is simplistic and cumbersome; 这简单而繁琐; if you are using this feature extensively, you could refactor the inner ChangeListener s to some common type, or some other clever solution. 如果您正在广泛使用此功能,则可以将内部ChangeListener重构为某种常见类型或其他一些聪明的解决方案。

I tested the code above with the following main (you wil have to provide a BigDecimal getTotalValue() method, in my case I just returned a constant BigDecimal ): 我用以下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() + "%");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM