簡體   English   中英

如何綁定列表<string>到 ObservableList<string> ?</string></string>

[英]How to bind a List<String> to a ObservableList<String>?

我有一個包含List<String>的 object Bean 我想將此列表“綁定”到ObservableList ,以便在將項目添加到原始列表或從原始列表中刪除時,更新ObservableList (然后觸發監視ObservableList的偵聽器)。

我發現了這個問題,它的答案顯示了如何使用JavaBeanStringPropertyBuilder將一個簡單的String包裝到一個 JavaFX StringProperty中。

我嘗試做同樣的事情,但將String替換為List<String> ,如下所示:

public class Bean {

    private final List<String> nameList;
    private final PropertyChangeSupport propertySupport ;

    public Bean() {
        this.nameList = new ArrayList<>();
        this.propertySupport = new PropertyChangeSupport(this);
    }

    public List<String> getNameList() {
        return nameList;
    }

    public void setNameList(List<String> nameList)
    {
        List<String> oldList = new ArrayList<>(this.nameList);
        this.nameList.clear();
        this.nameList.addAll(nameList);
        propertySupport.firePropertyChange("nameList", oldList, this.nameList);
    }

    public void addName(String name) {
        List<String> oldList = new ArrayList<>(this.nameList);
        this.nameList.add(name);
        propertySupport.firePropertyChange("nameList", oldList, this.nameList);
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        propertySupport.addPropertyChangeListener(listener);
    }
}
Bean bean = new Bean();
JavaBeanObjectProperty<List<String>> listProperty = null;
try
{
    listProperty = JavaBeanObjectPropertyBuilder.create().bean(bean).name("nameList").build();
} catch (NoSuchMethodException e)
{
    throw new RuntimeException(e);
}
listProperty.addListener((ObservableValue<? extends List<String>> obs, List<String> oldName, List<String> newName) ->
{
    System.out.println("List changed");
});

bean.setNameList(Arrays.asList("George", "James"));

但是在調用setNameList()之后沒有觸發監聽器,我不知道我錯過了什么。 請問你能幫幫我嗎?

使用Property<T>注冊的更改偵聽器只有在屬性值實際更改時才會收到通知。 也就是說, set(T newValue)方法是這樣實現的:

public void set(T newValue) {
    T oldValue = this.get();
    if (! oldValue.equals(newValue)) {
        // notify change listeners...
    }
}

JavaBeanObjectPropertyBuilder將創建一個JavaBeanObjectProperty<List<String>>Property<List<String>>的實現)並將其值設置為調用bean.getNameList()的結果。 bean.nameList listProperty引用。

JavaBeanObjectProperty還通過調用bean.addPropertyChangeListener(...)注冊了一個監聽器。 什么時候

propertySupport.firePropertyChange("nameList", oldList, this.nameList);

被調用時, JavaBeanObjectProperty中的內部偵聽器會將其自己的值設置為屬性更改支持觸發的新值; 即它會打電話

set(bean.nameList);

但是,由於這只是對屬性當前值的引用,因此不會通知注冊到listProperty的更改偵聽器(基本上沒有發生更改)。

澄清一下,如果有幫助: listProperty.get()返回的List<String>的內容將在您調用時更改

this.nameList.clear();

this.nameList.addAll(nameList);

bean中(因為listProperty引用了bean.nameList ),但實際的列表引用本身並沒有改變。

你可以測試這個,例如

Bean bean = new Bean();
JavaBeanObjectProperty<List<String>> listProperty = null;
try
{
    listProperty = JavaBeanObjectPropertyBuilder.create().bean(bean).name("nameList").build();
} catch (NoSuchMethodException e)
{
    throw new RuntimeException(e);
}
listProperty.addListener((ObservableValue<? extends List<String>> obs, List<String> oldName, List<String> newName) ->
{
    System.out.println("List changed");
});

List<String> oldList = listProperty.get();

bean.setNameList(Arrays.asList("George", "James"));

List<String> newList = listProperty.get();

System.out.println(oldList);
System.out.println(newList);
System.out.println(oldList == newList);

最好的解決方法是簡單地在Bean class 中使用ObservableList

public class Bean {

    private final ObservableList<String> nameList;

    public Bean() {
        this.nameList = FXCollections.observableArrayList<>();
    }

    public ObservableList<String> getNameList() {
        return nameList;
    }


    public void addName(String name) {
        this.nameList.add(name);
    }

}

請注意,您不會失去setNameList(...)提供的功能; 你可以做

bean.getNameList().setAll(...);

如果要設置列表的全部內容。 如果您想要相同的 API,您可以使用ListProperty而不是ObservableList

然后你的測試代碼就變成了

Bean bean = new Bean();

bean.getNameList().addListener((ListChangeListener.Change<? extends String> change) ->
{
    System.out.println("List changed");
});

bean.getNameList().setAll("George", "James");

正如問題的評論中所述,我真的不明白有任何限制阻止在 model 中使用ObservableList 事實上,這正是設計ObservableList (以及屬性和綁定 API)的用例。

沒有像 Java 用於簡單屬性的 Bean 適配器那樣設計用於可觀察列表的適配器。 因此,如果您真的想避免在 model class 中使用ObservableList (同樣,這對我來說真的沒有意義),您必須為Bean實現自己的偵聽器通知:

public class Bean {

    private final List<String> nameList ;
    private final List<Consumer<String>> nameAddedListeners ;
    private final List<Consumer<List<String>>> nameListReplacedListeners ;

    public Bean() {
        this.nameList = new ArrayList<>();
        this.nameAddedListeners = new ArrayList<>();
        this.nameListReplacedListeners = new ArrayList<>();
    }

    public List<String> getNameList() {
        return nameList ;
    }

    public void setNameList(List<String> newNames) {
        this.nameList.setAll(newNames);
        nameListReplacedListeners.forEach(listener -> listener.accept(newNames));
    }

    public void addName(String name) {
        this.nameList.add(name);
        nameAddedListeners.forEach(listener -> listener.accept(name));
    }

    public void addNameListReplacedListener(Consumer<List<String>> listener) {
        nameListReplacedListeners.add(listener);
    }

    public void addNameAddedListener(Consumer<String> listener) {
        nameAddedListeners.add(listener);
    }
}

現在你可以做

Bean bean = new Bean();
bean.addNameListReplacedListener(list -> System.out.println("Names changed"));
bean.setNameList(List.of("George", "James"));

或者您可以有效地創建一個適配器:

Bean bean = new Bean();
ObservableList<String> names = FXCollections.observableArrayList(bean.getNameList());
bean.addNameAddedListener(names::add);
bean.addNameListReplacedListener(names::setAll);

等等

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM