Планирование JavaBean с помощью свойств JavaFX

Я хочу использовать свойства JavaFX для привязки UI, но я не хочу их в своих классах моделей (см. Использование свойств javafx.beans в классах моделей). Мои классы моделей имеют getters и seters, и я хочу создавать свойства на их основе. Например, если предположить экземпляр bean с методами String getName() и setName(String name), я бы написал

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

ожидая, что property.set("Foobar") вызовет вызов bean.setName. Но это, похоже, не работает. Что мне не хватает?

Ответы

Ответ 1

Классы Simple*Property являются полными, автономными реализациями соответствующих абстрактных классов Property и не зависят от каких-либо других объектов. Так, например, SimpleStringProperty содержит поле (private) String, которое содержит текущее значение свойства.

Параметры конструктора, который вы показали:

new SimpleStringProperty(bean, "name")

являются:

  • bean: bean, к которому принадлежит свойство, если любой
  • name: имя свойства

bean может быть полезен в методе ChangeListener changed(...), так как вы можете получить "владение 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 поддерживает PropertyChangeListener s, JavaBeanStringProperty зарегистрирует PropertyChangeListener с помощью bean. Любые изменения в свойстве name Java bean будут переведены с помощью изменений JavaBeanStringProperty в свойства JavaFX. Следовательно, если базовый JavaBean поддерживает PropertyChangeListener s, то изменяется на bean через

bean.setName(...);

приведет к любым ChangeListener (или InvalidationListener s), зарегистрированным с сообщением JavaBeanStringProperty об изменении.

Итак, например, если класс 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 не поддерживает PropertyChangeListener s, изменения в bean через bean.setName(...) не будут передаваться в ChangeListener или InvalidationListener, зарегистрированном с помощью JavaBeanStringProperty.

Итак, если 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