如何在JavaFX中绑定双向嵌套属性?

时间:2014-05-05 15:45:48

标签: java binding javafx javabeans

如何在JavaFX中双向绑定嵌套属性?

例如,我有一个对象p,它有prop1prop2,后者又有value属性。

如何双向绑定它们,以便它们约束相等?

package tests.javafx.beans.binding;

import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;

public class Try_BindNested {

    public static class Nested {

        private SimpleDoubleProperty value = new SimpleDoubleProperty();

        public double getValue() {
            return value.get();
        }

        public void setValue(double value) {
            this.value.set(value);
        }

        public SimpleDoubleProperty valueProperty() {
            return value;
        }

    }


    public static class Parent {

        private SimpleObjectProperty<Nested> prop1 = new SimpleObjectProperty<Nested>();

        private SimpleObjectProperty<Nested> prop2 = new SimpleObjectProperty<Nested>();

        public Nested getProp1() {
            return prop1.get();
        }

        public void setProp1(Nested prop1) {
            this.prop1.set(prop1);
        }

        public SimpleObjectProperty<Nested> prop1Property() {
            return prop1;
        }

        public Nested getProp2() {
            return prop2.get();
        }

        public void setProp2(Nested prop1) {
            this.prop2.set(prop1);
        }

        public SimpleObjectProperty<Nested> prop2Property() {
            return prop2;
        }

    }

    public static void main(String[] args) {


        Parent p = new Parent();

        // how to bind bidirectional p.prop1.value = p.prop2.value?


    }

}

1 个答案:

答案 0 :(得分:6)

假设您要处理对&#34;中间&#34;的更改属性(即p.prop2.value如果你p.setProp1(...);更新,则会更新)然后无法直接使用绑定进行此操作;你必须使用一对听众。

使用标准JavaFX属性API:

ObservableDoubleValue prop1Value = Bindings.selectDouble(p.prop1Property(), "value");
ObservableDoubleValue prop2Value = Bindings.selectDouble(p.prop2Property(), "value");
prop1Value.addListener(new ChangeListener<Number>() {
   @Override
   public void changed(ObservableValue<? extends Number> ov, Number oldVal, Number newValue) {
        p.getProp2().setValue(newValue);
   }
});
prop2Value.addListener(new ChangeListener<Number>() {
   @Override
   public void changed(ObservableValue<? extends Number> ov, Number oldVal, Number newValue) {
        p.getProp1().setValue(newValue);
   }
});

Bindings.select方法有点难看(它们依赖于反射并且不是类型安全的);此外,在早期的JavaFX 8版本中,如果任何中间属性为null,它们会发出各种警告(尽管这是根据API文档支持的用例)。如果这成为问题,您可以查看EasyBind framework。你仍然需要&#34;双听者&#34;但是,这里也有成语。

没有&#34;内置的原因&#34;对于这种情况,双向绑定是关于如何更新属性的模糊不清。通常,您可能需要p.getProp1().setValue(newVal);之类的内容,或者您​​可能想要更改中间属性:p.setProp1(new Nested(newVal));

最后,您可能需要小心使用浮点类型。您不能使用上面的代码进行无限递归的原因是set中的DoubleProperty方法在通知更改侦听器之前检查是否确实发生了更改。如果该值涉及任何计算,则存在舍入错误的风险,从而导致属性检查相等性失败的情况,因此您可能最终得到一个很好的StackOverflowError。如果您的value属性执行的操作比简单地存储值更复杂,那么您可能希望使用容差来保护对setValue的调用:

private static final double TOLERANCE = 1e-16 ; // or some other suitable small number

prop1Value.addListener(new ChangeListener<Number>() {
   @Override
   public void changed(ObservableValue<? extends Number> ov, Number oldVal, Number newValue) {
        if (Math.abs(p.getProp2.getValue() - newVal) > TOLERANCE) {
            p.getProp2().setValue(newValue);
        }
   }
});

同样在另一个方向。