ChangeListener 没有给出正确的旧值

ChangeListener not giving correct old value

代码如下:

package sample;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

/**
 * Created by IDEA on 29/07/15.
 */
public class ListPropertyTest {
    public static void main(String[] args) {
        ListProperty<String> lp =
                new SimpleListProperty<>(FXCollections.observableArrayList());
        lp.addListener(new ListChangeListener<String>() {
            @Override
            public void onChanged(Change<? extends String> c) {
                while(c.next()) {
                    String action = c.wasPermutated() ? "perm"
                            : c.wasUpdated() ? "upd"
                            : c.wasRemoved() ? "rem"
                            : c.wasAdded() ? "add" : "";
                    System.out.println("Action: " + action);
                    System.out.println("Removed: " + c.getRemoved());
                    System.out.println("Added: " + c.getAddedSubList());
                }
            }
        });

        lp.addListener(new ChangeListener<ObservableList<String>>() {
            @Override
            public void changed(ObservableValue<? extends ObservableList<String>> observable, ObservableList<String> oldValue, ObservableList<String> newValue) {
                System.out.println("List changed.");
                System.out.println("Old: " + oldValue);
                System.out.println("New: " + newValue);
            }
        });

        lp.addListener(new InvalidationListener() {
            @Override
            public void invalidated(Observable observable) {
                System.out.println("List invalid");
            }
        });

        System.out.println("Add =========");
        lp.addAll("one", "two");
        System.out.println("Set =========");
        lp.set(FXCollections.observableArrayList("two", "three"));
        System.out.println("Remove ============");
        lp.remove("two");
    }
}

结果:

Add =========
List invalid
List changed.
Old: [one, two]
New: [one, two]
Action: add
Removed: []
Added: [one, two]
Set =========
List invalid
List changed.
Old: [one, two]
New: [two, three]
Action: rem
Removed: [one, two]
Added: [two, three]
Remove ============
List invalid
List changed.
Old: [three]
New: [three]
Action: rem
Removed: [two]
Added: []

如您所见,更改侦听器仅在 "Set" 部分表现正确。

其中大部分是预期行为。

一个ListProperty既是ObjectProperty<ObservableList>又是ObservableList

作为 ObjectProperty<ObservableList> 意味着它 包含 (包含对 ObservableList 的引用并且具有 setValue(ObservableList)(或 set(...)) 和 getValue() 方法。作为一个ObjectProperty<ObservableList>,你可以用它注册一个ChangeListener<ObservableList>。如果 对包装列表 的引用发生变化,由某人调用 setValue(ObservableList),则 ChangeListener<ObservableList>.changed(...) 方法会在任何已注册的 ChangeListener 上调用,并传入一个引用ListProperty 本身、旧的 ObservableList 引用和新的 ObservableList 引用。当您在代码中调用 set 时,您会看到此行为。

然而,当您简单地更改列表的内容时,包装列表仍然是相同的物理对象。所以 "old list" 和 "new list" 是一回事。在这种情况下,更改侦听器触发通知是 bug;感谢@kleopatra 指出这一点。

A ListProperty 还通过将 ObservableList 方法调用委托给包装列表实例来实现 ObservableList。这意味着如果列表的内容发生变化,任何在 ListProperty 注册的 ListChangeListener 都会收到通知。再次注意,在这种情况下只有一个列表对象;只是修改了内容

您可以通过传递给 onChanged(...) 方法的 Change 对象获取有关 ObservableList 发生的更改的详细信息,正如您在 [=35= 的输出中观察到的那样] 方法。请注意,c.getFrom()c.getTo() 也会为您提供已更改的项目的索引。

我几乎从来不需要 ListProperty:在绝大多数用例中,我只使用普通的 ObservableList 实现就足够了。基于对象列表的控件(例如 ListViewTableView)使用它们,因此您可以调用 setItems(...) 以在需要时传入现有的 ObservableList,或者(可能更常见)你可以调用 getItems() 来检索当前列表并修改它。从上面的解释中可以看出,ListProperty 允许这些控件观察任何一种变化。但很可能在您自己的代码中,您直接使用 ObservableList 的频率远高于使用 ListProperty.

的频率。