0

目前,我想做的是使用 JAXB 生成的 POJO 将每个 Java 属性绑定到 JavaFX 组件。为此,我按照以下方式进行:

  • 我更改了 JAXB 的默认生成以添加PropertyChangeSupport以使 POJO 支持绑定。
  • 我创建了一种工厂,它在输入中获取一个类实例并返回一个 Map,其中是属性本身,JavaFX 组件的值与属性的值绑定
  • 返回的地图显示在 JFXPanel 中。

这是我工厂的样品:

public static Map<Field, Node> createComponents(Object obj) throws NoSuchMethodException
{
        Map<Field, Node> map = new LinkedHashMap<Field, Node>();

        for (final Field field : obj.getClass().getDeclaredFields())
        {
            @SuppressWarnings("rawtypes")
            Class fieldType = field.getType();

            if (fieldType.equals(boolean.class) || (fieldType.equals(Boolean.class))) //Boolean
            {
                map.put(field, createBool(obj, field));
            }
            else if (fieldType.equals(int.class) || (fieldType.equals(Integer.class))) //Integer
            {
               map.put(field, createInt(obj, field));
            }
            else if (fieldType.equals(BigInteger.class)) //BigInteger
            {
               map.put(field, createBigInt(obj, field));
            }
            else if (fieldType.equals(long.class) || fieldType.equals(Long.class)) //Long
            {
               map.put(field, createLong(obj, field));
            }
            else if (fieldType.equals(String.class)) //String
            {
               map.put(field, createString(obj, field));

            }
            ...
        }
        return map;   
} 

public static Node createBool(Object obj, final Field field) throws NoSuchMethodException
{

      System.out.println(field.getType().getSimpleName() + " spotted");
      JavaBeanBooleanProperty boolProperty = JavaBeanBooleanPropertyBuilder.create().bean(obj).name(field.getName()).build();
      boolProperty.addListener(new ChangeListener<Boolean>() {
         @Override
         public void changed(ObservableValue<? extends Boolean> arg0, Boolean arg1, Boolean arg2)
         {
            prettyPrinter(field, arg1, arg2);
         }
      });
      CheckBox cb = new CheckBox();
      cb.setText(" : " + field.getName());
      cb.selectedProperty().bindBidirectional(boolProperty);
      return cb;

}

public static Node createInt(Object obj, final Field field) throws NoSuchMethodException
{
      System.out.println(field.getType().getSimpleName() + " spotted");
      JavaBeanIntegerProperty intProperty = JavaBeanIntegerPropertyBuilder.create().bean(obj).name(field.getName()).build();
      StringProperty s = new SimpleStringProperty();
      StringConverter sc = new IntegerStringConverter();
      Bindings.bindBidirectional(s, intProperty, sc);
      s.addListener(new ChangeListener<String>() {
         @Override
         public void changed(ObservableValue<? extends String> arg0, String arg1, String arg2)
         {
            prettyPrinter(field, arg1, arg2);
         }
      });

      TextField tf = new TextField();
      tf.textProperty().bindBidirectional(s);

      return tf;

}

所以,我遇到的问题是:在大多数情况下,例如,当我更改一个 textField 时,POJO 属性不会注意到。但在某些情况下,当我更改 POJO 中字段的顺序时,每个侦听器都会注意到任何更改。

下面是一个 GUI 看起来像跟随Personne类的示例(当前有效)

图形用户界面示例

public class Personne
{

   private int                   taille;

   private Boolean               lol;

   private long                  pointure;

   private BigInteger            age;

   private boolean               zombified;

   private String                name;

   private PropertyChangeSupport _changeSupport = new PropertyChangeSupport(this);

   public Boolean getLol()
   {
      return this.lol;
   }

   public long getPointure()
   {
      return this.pointure;
   }

   public int getTaille()
   {
      return taille;
   }

   public boolean getZombified()
   {
      return zombified;
   }

   public BigInteger getAge()
   {
      return age;
   }

   public String getName()
   {
      return name;
   }

   public void setPointure(long pointure)
   {

      final long prev = this.pointure;
      this.pointure = pointure;
      _changeSupport.firePropertyChange("pointure", prev, pointure);
   }

   public void setTaille(int taille)
   {
      final int prev = this.taille;
      this.taille = taille;
      _changeSupport.firePropertyChange("taille", prev, taille);
   }

   public void setAge(BigInteger age)
   {
      final BigInteger prev = this.age;
      this.age = age;
      _changeSupport.firePropertyChange("age", prev, age);

   }

   public void setName(String name)
   {
      final String prev = this.name;
      this.name = name;
      _changeSupport.firePropertyChange("name", prev, name);
   }

   public void setLol(Boolean lol)
   {
      final Boolean prev = this.lol;
      this.lol = lol;
      _changeSupport.firePropertyChange("lol", prev, lol);
   }

   public void setZombified(boolean zombified)
   {
      final boolean prev = this.zombified;
      this.zombified = zombified;
      _changeSupport.firePropertyChange("zombified", prev, zombified);

   }

   public void addPropertyChangeListener(final PropertyChangeListener listener)
   {
      _changeSupport.addPropertyChangeListener(listener);
   }
}

我想知道属性顺序如何影响这样的绑定。此外,我注意到如果我想返回包装在 HBox 中的节点,则绑定不再起作用。

我认为我做错了什么,但我不知道是什么。

4

1 回答 1

1

您的 JavaBeanIntegerProperty 和 JavaBeanBooleanProperty 过早地被垃圾收集。

该方法Property.bind(Observable)使属性持有对可观察对象的强引用,但可观察对象只持有对属性的弱引用!(它在 Observable 上注册了一个 Listener,只有一个 WeakReference 返回到 Property)。同样,当您调用 时Bindings.bindBidirectional(Property, Property)两个属性都持有对彼此的弱引用!这是一个非常重要的细节,在文档中很难找到。

如果您只与 JavaFX 对象交互,这不是问题,因为 JavaFX 对象自然拥有对其所有属性的强引用。但是如果你使用 JavaBeanIntegerProperty 来包装遗留对象的 bean 属性,那么 bean 不会持有对 JavaBeanIntegerProperty 的强引用,所以在 gc 之后,Property 将消失并停止更新 bean!我认为这是 JavaBeanIntegerProperty 中的设计错误。

解决方案是将 JavaBeanIntegerProperty 分配给一个字段并存储它,只要您希望绑定不断更新 bean。

或者,您可以编写自己的 Property 子类来确保从 bean 到 Property 存在强引用(例如 addPropertyChangeListener 到 bean 以永久监听)。

于 2014-05-28T20:52:45.277 回答