使用 JavaFX 属性包装 JavaBean

Posted

技术标签:

【中文标题】使用 JavaFX 属性包装 JavaBean【英文标题】:JavaBean wrapping with JavaFX Properties 【发布时间】:2014-06-24 16:27:51 【问题描述】:

我想将 JavaFX 属性用于 UI 绑定,但我不想在我的模型类中使用它们(请参阅 Using javafx.beans properties in model classes)。我的模型类有 getter 和 setter,我想基于它们创建属性。例如,假设一个实例 bean 具有 String getName()setName(String name) 方法,我会写

 SimpleStringProperty property = new SimpleStringProperty(bean, "name")

预计property.set("Foobar") 会触发对bean.setName 的调用。但这似乎不起作用。我错过了什么?

【问题讨论】:

不要完全放弃在模型类中使用 javafx 属性的想法。我为您链接的问题添加了一个替代答案:这可能是一种值得考虑的方法。 【参考方案1】:

Simple*Property 类是其对应的Property 抽象类的完整独立实现,不依赖于任何其他对象。因此,例如,SimpleStringProperty 包含一个(私有)String 字段本身,它包含属性的当前值。

你展示的构造函数的参数:

new SimpleStringProperty(bean, "name")

是:

bean: 属性所属的bean,如果有的话 name: 属性名称

beanChangeListenerchanged(...) 方法中很有用,因为您可以检索从属性本身更改的属性的“拥有 bean”。 name 可以类似地使用(如果你有同一个监听器注册了多个属性,你可以找出哪个属性改变了:虽然我从不使用这种模式)。

因此,SimpleStringProperty 作为对象的可观察属性的典型用法如下所示:

public class Person 
    private final StringProperty firstName 
        = new SimpleStringProperty(this, "firstName");

    public final String getFirstName() 
        return firstName.get();
    

    public final void setFirstName(String firstName) 
        this.firstName.set(firstName);
    

    public StringProperty firstNameProperty() 
        return firstName ;
    

    // ... other properties, etc

您正在寻找的功能:将现有的 Java Bean 样式属性包装在 JavaFX 可观察属性中是由 javafx.beans.property.adapter 包中的类实现的。因此,例如,您可以这样做

StringProperty nameProperty = new JavaBeanStringPropertyBuilder()
        .bean(bean)
        .name("name")
        .build();

打电话

nameProperty.set("James");

使用此设置将有效地导致调用

bean.setName("James");

如果 bean 支持 PropertyChangeListeners,JavaBeanStringProperty 将向 bean 注册一个 PropertyChangeListener。对 Java Bean 的 name 属性的任何更改都将由 JavaBeanStringProperty 转换为 JavaFX 属性更改。因此,如果底层的JavaBean支持PropertyChangeListeners,那么通过

bean.setName(...);

将导致向JavaBeanStringProperty 注册的任何ChangeListeners(或InvalidationListeners)收到更改通知。

所以,例如,如果 Bean 类是

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

public class Bean 

    private String name ;
    private final PropertyChangeSupport propertySupport ;

    public Bean(String name) 
        this.name = name ;
        this.propertySupport = new PropertyChangeSupport(this);
    

    public Bean() 
        this("");
    

    public String getName() 
        return name ;
    

    public String setName(String name) 
        String oldName = this.name ;
        this.name = name ;
        propertySupport.firePropertyChange("name", oldName, name);
    

    public void addPropertyChangeListener(PropertyChangeListener listener) 
        propertySupport.addPropertyChangeListener(listener);
    

然后是下面的代码:

Bean bean = new Bean();
StringProperty nameProperty() = new JavaBeanStringPropertyBuilder()
        .bean(bean)
        .name("name")
        .build();
nameProperty().addListener((obs, oldName, newName) -> System.out.println("name changed from "+oldName+" to "+newName));
bean.setName("James");
System.out.println(nameProperty().get());

将产生输出:

name changed from to James 
James

如果 JavaBean 不支持 PropertyChangeListeners,则通过 bean.setName(...) 对 bean 所做的更改将不会传播到使用 JavaBeanStringProperty 注册的 ChangeListeners 或 InvalidationListeners。

所以如果 bean 是简单的

public class Bean 

    public Bean() 
        this("");
    

    public Bean(String name) 
        this.name = name ;
    

    private String name ;

    public String getName() 
        return name ;
    

    public void setName(String name) 
        this.name = name ;
    

JavaBeanStringProperty 将无法观察更改,因此更改侦听器永远不会通过调用bean.setName() 来调用。所以上面的测试代码会简单的输出

James

【讨论】:

+1 很好的解释。 JavaBeanStringPropertyBuilder 将为 bean 添加 PropertyChangeListener 支持。从答案中我觉得用户应该将自己的支持添加到 bean 中。 不完全是:JavaBeanStringProperty 没有为 bean 添加PropertyChangeListener 支持。答案已更新以澄清。 如果我要求您也展示一个带有和不带有 PropertyChangeListener 支持的示例 Java Bean,您介意吗? @James_D,谢谢。集合属性是否有类似的优雅解决方案,例如 List 耻辱必须依赖 java.desktop 模块(包括 Swing、AWT)才能使用它

以上是关于使用 JavaFX 属性包装 JavaBean的主要内容,如果未能解决你的问题,请参考以下文章

JavaFX - 属性“无效”的含义

使用 Launch4j VM 参数路径包装的 JavaFX 项目

JavaFX:使用对象属性的组合框

如何使用 Platform.runLater 使 JavaFX 线程安全

在模型类中使用 javafx.beans 属性

javaFX中是不是有等效的枚举属性类型?