web-dev-qa-db-ja.com

JavaFXプロパティを使用したJavaBeanラッピング

UIバインディングにJavaFXプロパティを使用したいのですが、モデルクラスにそれらを使用したくありません( モデルクラスでのjavafx.beansプロパティの使用 を参照)。私のモデルクラスにはゲッターとセッターがあり、それらに基づいてプロパティを作成したいと思います。たとえば、メソッドString getName()setName(String name)を持つインスタンスbeanを想定すると、次のようになります。

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

property.set("Foobar")が_bean.setName_への呼び出しをトリガーすることを期待します。しかし、これはうまくいかないようです。何が足りないのですか?

22
warakawa

_Simple*Property_クラスは、対応するProperty抽象クラスの完全なスタンドアロン実装であり、他のオブジェクトに依存しません。したがって、たとえば、SimpleStringPropertyには、プロパティの現在の値を保持する(プライベート)Stringフィールド自体が含まれます。

示したコンストラクターのパラメーター:

_new SimpleStringProperty(bean, "name")
_

は:

  • bean:プロパティが属するBean(存在する場合)
  • name:プロパティの名前

beanは、プロパティ自体から変更されたプロパティの「所有Bean」を取得できるため、ChangeListenerchanged(...)メソッドで役立ちます。 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をサポートしている場合、JavaBeanStringPropertyPropertyChangeListenerをBeanに登録します。 Java Beanのnameプロパティへの変更は、JavaBeanStringPropertyによってJavaFXプロパティの変更に変換されます。したがって、基盤となるJavaBeanがPropertyChangeListenersをサポートしている場合は、経由でBeanに変更されます。

_bean.setName(...);
_

ChangeListenerに登録されているInvalidationListeners(またはJavaBeanStringPropertys)には、変更が通知されます。

したがって、たとえば、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への変更は、ChangeListenersまたはInvalidationListenerに登録されているJavaBeanStringPropertysに伝播されません。

つまり、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
_
46
James_D