JavaFX中的自定义双向绑定 [英] Custom bidirectional bindings in JavaFX

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

问题描述

我正在尝试实现一个涉及2个字段的计算的GUI.我的模型有2个属性和1个绑定.

I'm trying to implements a GUI involving a computation of 2 fields. My model has 2 properties and one binding.

ObjectProperty<BigDecimal> price = new SimpleObjectProperty<>();
ObjectProperty<BigDecimal> quantity= new SimpleObjectProperty<>();
ObjectBinding<BigDecimal> totalPrice = new ObjectBinding<BigDecimal>() {
    { bind(price,quantity);}
    protected BigDecimal computeValue() {
        if (price.get() == null || quantity.get() == null) return null;
        return price.get().multiply(quantity.get());
    }
};

我的GUI有3个 TextField 以匹配价格,金额和totalPrice.通常,我正在属性与我的 TextField 之类的常规绑定

My GUI has 3 TextField to match price, amount, totalPrice. Usualy, I'm doing a regular binding between my properties and my TextField as such

priceTextField.textProperty().bindBidirectional(myModel.priceProperty(), new NumberStringConverter());

现在这有点棘手.如果用户修改价格或数量,则必须更新TotalPrice(这是绑定到目前为止所做的事情).但我希望能够执行以下操作:如果用户更新TotalPrice,则它将相应地将数量重新计算为固定价格.

Now this where it gets a bit tricky. If the user modify the price or the quantity, it has toupdate the TotalPrice (which is what the binding does so far). But I want to be able to do the following: if the user update the TotalPrice, then it recomputes the quantity accordingly to the fixed price.

所以问题是:如何创建这样的流=> TotalPrice绑定价格和数量,但是数量绑定TotalPrice和价格.当我在 totalPriceTextfield 中输入内容时,应该更新 quantityTextField ,反之亦然.

So the question is: how do I create such a flow => TotalPrice is bind on price and quantity, but quantity is bind on TotalPrice and price. When I input something in the totalPriceTextfield it should update the quantityTextField and vice versa.

谢谢.

******编辑********这是一段丑陋的代码,只是为了说明我想要实现的目标(nb:我知道我可以使用 Binding.multiply 和其他方法,但我需要将来的项目来实现计算功能)

****** Edit ******** Here is an ugly piece of code just to illustrate what I want to achieve (nb: i know i could use Binding.multiply and other method but I need for future project to implement the compute function)

public class TestOnBindings {

    private DoubleProperty price = new SimpleDoubleProperty(10.0);
    private DoubleProperty quantity = new SimpleDoubleProperty(1.0);
    private DoubleProperty total = new SimpleDoubleProperty(1.0);

    private DoubleBinding totalBinding = new DoubleBinding() {
        {bind(quantity,price);}
        @Override
        protected double computeValue() {
            return quantity.get()*price.get();
        }
    };

    private DoubleBinding quantityBinding = new DoubleBinding() {
        {bind(total,price);}
        @Override
        protected double computeValue() {
            return total.get()/price.get();
        }
    }; 


    public TestOnBindings(){
        total.bind(totalBinding); //should really not do that, looks ugly
        quantity.bind(quantityBinding); //now you're asking for troubles
    }


    public void setPrice(Double price){
        this.price.set(price);
    }


    public void setQuantity(Double quantity){
        this.quantity.set(quantity);
    }

    public void setTotal(Double total){
        this.total.set(total);
    }


    public Double getTotal(){
        return total.get();
    }


    public Double getQuantity(){
        return quantity.get();
    }

    public static void main(String[] args) {
        TestOnBindings test = new TestOnBindings();
        test.setQuantity(5.0);

        System.out.println("Total amount = " + test.getTotal());
    }

}

和明显的错误:

线程"main"中的异常java.lang.RuntimeException:无法设置绑定值.在javafx.beans.property.DoublePropertyBase.set(DoublePropertyBase.java:142)

Exception in thread "main" java.lang.RuntimeException: A bound value cannot be set. at javafx.beans.property.DoublePropertyBase.set(DoublePropertyBase.java:142)

推荐答案

我认为我的问题已经在另一个

I think that my question has already been answered in another post and that this guy provided a simple way on his blog

我对他的课程进行了一些修改,以适应我的需要,并开设了一个非常易于使用的课程:

I modified a bit his class to adapt my needs and have a very easy to use class:

public class CustomBinding {

    public static <A ,B> void bindBidirectional(Property<A> propertyA, Property<B> propertyB, Function<A,B> updateB, Function<B,A> updateA){
        addFlaggedChangeListener(propertyA, propertyB, updateB);
        addFlaggedChangeListener(propertyB, propertyA, updateA);
    }

    public static <A ,B> void bind(Property<A> propertyA, Property<B> propertyB, Function<A,B> updateB){
        addFlaggedChangeListener(propertyA, propertyB, updateB);
    }

    private static <X,Y> void addFlaggedChangeListener(ObservableValue<X> propertyX, WritableValue<Y> propertyY, Function<X,Y> updateY){
        propertyX.addListener(new ChangeListener<X>() {
            private boolean alreadyCalled = false;

            @Override
            public void changed(ObservableValue<? extends X> observable, X oldValue, X newValue) {
                if(alreadyCalled) return;
                try {
                    alreadyCalled = true;
                    propertyY.setValue(updateY.apply(newValue));
                }
                finally {alreadyCalled = false; }
            }
        });
    }
}

然后将其应用到我的示例中....仍然需要进行一些微调,但这确实可以完成工作.

and then applying it to my example.... Still need a little bit of fine tuning, but it does the job.

public class TestOnBindings {

    private DoubleProperty price = new SimpleDoubleProperty(10.0);
    private DoubleProperty quantity = new SimpleDoubleProperty(1.0);
    private DoubleProperty total = new SimpleDoubleProperty(1.0);

    public TestOnBindings(){
        CustomBinding.<Number,Number>bindBidirectional(quantity, total, 
                (newQuantity)-> newQuantity.doubleValue() * price.get(),
                (newTotal)-> newTotal.doubleValue() /price.get());

        CustomBinding.<Number,Number>bind(price, total, 
                (newPrice)-> newPrice.doubleValue() * quantity.get());
    }

    public void setPrice(Double price){this.price.set(price);}
    public void setQuantity(Double quantity){this.quantity.set(quantity);}
    public void setTotal(Double total){this.total.set(total);}

    public Double getTotal(){return total.get();}
    public Double getQuantity(){return quantity.get();}
    public Double getPrice(){return price.get();}


    public static void main(String[] args) {
        TestOnBindings test = new TestOnBindings();

        test.setQuantity(5.0);

        System.out.println("Quantity = " + test.getQuantity());
        System.out.println("Price = " + test.getPrice());
        System.out.println("Total = " + test.getTotal());

        test.setTotal(60.0);

        System.out.println("---------------------------------------------");
        System.out.println("Quantity = " + test.getQuantity());
        System.out.println("Price = " + test.getPrice());
        System.out.println("Total = " + test.getTotal());

        test.setPrice(5.0);

        System.out.println("---------------------------------------------");
        System.out.println("Quantity = " + test.getQuantity());
        System.out.println("Price = " + test.getPrice());
        System.out.println("Total = " + test.getTotal());
    }

}

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

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