简体   繁体   中英

ChangeListener not giving correct old value

Here is the code:

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");
    }
}

Result:

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: []

As you can see, the change listener behaved correctly only in the "Set" part.

Most of this is intended behavior.

A ListProperty is both an ObjectProperty<ObservableList> and an ObservableList .

Being an ObjectProperty<ObservableList> means it wraps (contains a reference to) an ObservableList and has setValue(ObservableList) (or set(...) ) and getValue() methods. As an ObjectProperty<ObservableList> , you can register a ChangeListener<ObservableList> with it. If the reference to the wrapped list changes, by someone calling setValue(ObservableList) , the ChangeListener<ObservableList>.changed(...) method gets called on any registered ChangeListener , passing in a reference to the ListProperty itself, the old ObservableList reference, and the new ObservableList reference. You see this behavior when you call set in your code.

When you simply change the content of the list, however, the wrapped list is still the same physical object. So the "old list" and the "new list" are one and the same. It is a bug that the change listener fires notifications in this case; hat-tip to @kleopatra for pointing this out.

A ListProperty also implements ObservableList , by delegating the ObservableList method calls to the wrapped list instance. This means if the content of the list change, any ListChangeListener s registered with the ListProperty get notified. Again, note in this case that there is only one list object; it's just that its contents have been modified.

You can get details about changes that occur to an ObservableList via the Change object passed to the onChanged(...) method, as you observe in the output from your onChanged method. Note that c.getFrom() and c.getTo() will give you the indexes of the items that have changed as well.

I almost never have a need for a ListProperty : in the vast majority of use cases it is enough for me to just use a plain ObservableList implementation. Controls that are based on lists of objects (such as ListView and TableView ) use them, so you can call setItems(...) to pass in an existing ObservableList if you need, or (probably more usually) you can just call getItems() to retrieve the current list and modify it. As you can see from the explanation above, the ListProperty allows these controls to observe either kind of change. But it is likely that in your own code, you will use ObservableList directly far more often than you use a ListProperty .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM