在 JavaFX 中使用 ObjectBinding 进行双向绑定 [英] Bidirectional binding with ObjectBinding in JavaFX

查看:33
本文介绍了在 JavaFX 中使用 ObjectBinding 进行双向绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a simple bean that has some properties related with each other. For example, this bean has a property called discountRate and another called discountValue. The discountRate is the percentage (%) of discount applied to a sale. The discountValue is the value($) of discount applied to a sale. 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. 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;
    }
}

如您所见,我正在尝试在设置值时计算百分比,并在设置速率时计算值.我上面尝试的绑定无法绑定,因为这将进入一个永恒的循环.有什么办法可以通过绑定来解决这个问题,或者我需要在 setter 中进行计算?

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?

推荐答案

您将需要监听字段的更改,但要跟踪监听器是否已被触发,以免再次无限循环触发.灵感来自 JavaFX 的实际代码,反编译 此处.

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.

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 重构为某种常见类型或其他一些巧妙的解决方案.

This is simplistic and cumbersome; if you are using this feature extensively, you could refactor the inner ChangeListeners to some common type, or some other clever solution.

我使用以下主要测试了上面的代码(您必须提供一个 BigDecimal getTotalValue() 方法,在我的情况下,我只返回了一个常量 BigDecimal):

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):

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() + "%");
}

这篇关于在 JavaFX 中使用 ObjectBinding 进行双向绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆