2014-05-05 60 views
1

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

例如,我有具有化子性质prop1prop2,这反过来又具有属性value二者的对象p

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

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? 


    } 

} 

回答

5

假设你要处理变为“中间”属性(即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版本中,如果任何中间属性为空(尽管根据API文档支持的用例),它们会发出各种警告。如果这成为问题,您可以查看EasyBind framework。不过,你仍然需要这里的“双听众”成语。

对于这种情况没有“内置”双向绑定的原因是它对于如何更新属性是不明确的。一般情况下,您可能需要类似p.getProp1().setValue(newVal);的东西,或者您可能想要更改中间属性:p.setProp1(new Nested(newVal));

最后,您可能需要小心使用浮点类型来执行此操作。上述代码无法递归递归的原因在于DoubleProperty中的set方法会在通知更改侦听器之前检查确实发生了更改。如果该值涉及任何计算,则可能会产生舍入错误,从而创建属性检查相等性检查失败的情况,因此最终可能会出现StackOverflowError错误。如果你的价值属性做任何事情不是简单的存储值比较复杂,你可能想调用的公差后卫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); 
     } 
    } 
}); 
在其他方向

和类似。