Java 8:Observable List – 在属性更改时调用Invalidation Listener或Change Listener

我构建一个自定义属性并将其添加到可观察列表中。 但是如果属性内容发生更改,则不会调用侦听器。 以下代码段显示了“构建”:

public static final class TestObject { private final ReadOnlyStringWrapper title = new ReadOnlyStringWrapper(); private final BooleanProperty selected = new SimpleBooleanProperty(false); public TestObject(String title) { this.title.set(title); } public String getTitle() { return title.get(); } public ReadOnlyStringProperty titleProperty() { return title.getReadOnlyProperty(); } public boolean getSelected() { return selected.get(); } public BooleanProperty selectedProperty() { return selected; } public void setSelected(boolean selected) { this.selected.set(selected); } @Override public int hashCode() { return Objects.hash(title.get()); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } final TestObject other = (TestObject) obj; return Objects.equals(this.title.get(), other.title.get()); } @Override public String toString() { return "TestObject{" + "title=" + title.get() + ", selected=" + selected.get() + '}'; } } 

这是我的POJO类,其内部属性值如name和selected。

 public static final class TestProperty extends SimpleObjectProperty { public TestProperty(String name) { super(new TestObject(name)); init(); } public TestProperty(TestObject testObject) { super(testObject); init(); } public String getTitle() { return getValue().getTitle(); } public void setSelected(boolean selected) { getValue().setSelected(selected); } public boolean getSelected() { return getValue().getSelected(); } public BooleanProperty selectedProperty() { return getValue().selectedProperty(); } public ReadOnlyStringProperty titleProperty() { return getValue().titleProperty(); } @Override public void set(TestObject testObject) { super.set(testObject); init(); } @Override public void setValue(TestObject testObject) { super.setValue(testObject); init(); } private void init() { if (get() == null) return; get().titleProperty().addListener((v, o, n) -> fireValueChangedEvent()); get().selectedProperty().addListener((v, o, n) -> { fireValueChangedEvent(); }); } } 

这是我基于POJO的自定义属性。 所有属性更改都将触发我的自定义属性的更改事件。

 @Test public void testSimple() { final AtomicInteger counter = new AtomicInteger(0); final TestProperty testProperty = new TestProperty("Test"); testProperty.addListener(observable -> { System.out.println("New state: " + testProperty.get().toString()); counter.incrementAndGet(); }); testProperty.setSelected(true); testProperty.setSelected(false); Assert.assertEquals(2, counter.intValue()); } 

在此测试中,您可以看到属性更改事件正常工作。

 @Test public void testList() { final AtomicInteger counter = new AtomicInteger(0); final ObservableList observableList = new ObservableListWrapper(new ArrayList()); observableList.add(new TestProperty("Test 1")); observableList.add(new TestProperty("Test 2")); observableList.add(new TestProperty("Test 3")); observableList.addListener(new ListChangeListener() { @Override public void onChanged(Change change) { System.out.println("**************"); } }); observableList.addListener((Observable observable) -> { System.out.println("New state: " + ((TestProperty) observable).get().toString()); counter.incrementAndGet(); }); observableList.get(1).setSelected(true); observableList.get(2).setSelected(true); observableList.get(1).setSelected(false); observableList.get(2).setSelected(false); Assert.assertEquals(4, counter.intValue()); } 

但是在此代码中,您会看到,如果列表中的属性值已更改,则可观察列表不会调用失效侦听器,也不会调用更改侦听器。

哪里不对?

谢谢。

要创建一个可观察列表,如果列表元素的属性发生更改,将发送“列表更新”通知,您需要使用提取器创建列表。 extractor是一个Callback ,它将列表的每个元素映射到Observable的数组。 如果任何Observable更改,将通知在列表中注册的InvalidationListenerListChangeListener

所以在你的testList()方法中,你可以做到

 final ObservableList observableList = FXCollections.observableList( new ArrayList<>(), (TestProperty tp) -> new Observable[]{tp.selectedProperty()}); 

如果标题能够更改,并且您还希望列表在发生这种情况时接收通知,那么您也可以这样做:

 final ObservableList observableList = FXCollections.observableList( new ArrayList<>(), (TestProperty tp) -> new Observable[]{tp.selectedProperty(), tp.titleProperty()}); 

请注意,因为提取器是一个Callback (本质上是一个函数),所以实现可以是任意复杂的(有条件地根据另一个属性的值来观察一个属性,等等)。

只要列表中包含的属性被修改,ObservableList就不会通知侦听器,它会在通知列表时通知。

修改测试时可以看到:

 @Test public void testList() { final AtomicInteger counter = new AtomicInteger(0); final ObservableList observableList = new ObservableListWrapper<>(new ArrayList<>()); observableList.addListener(new ListChangeListener() { @Override public void onChanged(Change change) { System.out.println("**************"); counter.incrementAndGet(); } }); observableList.add(new TestProperty("Test 1")); observableList.add(new TestProperty("Test 2")); observableList.add(new TestProperty("Test 3")); observableList.get(1).setSelected(true); observableList.get(2).setSelected(true); observableList.get(1).setSelected(false); observableList.get(2).setSelected(false); Assert.assertEquals(3, counter.intValue()); } 

编辑:添加了一个示例ObserverListener装饰器,它根据OP的需要提供ObservableValue更改侦听器的自动注册/注销。

 /** * Decorates an {@link ObservableList} and auto-registers the provided * listener to all new observers, and auto-unregisters listeners when the * item is removed from the list. * * @param  */ public class ObservableValueList implements ObservableList { private final ObservableList list; private final ChangeListener valueListener; public ObservableValueList(ObservableList list, ChangeListener valueListener) { this.list = list; //list to existing contents of list this.list.stream().forEach((item) -> item.addListener(valueListener)); //register listener which will add/remove listner on change to list this.list.addListener((Change change) -> { change.getAddedSubList().stream().forEach( (item) -> item.addListener(valueListener)); change.getRemoved().stream().forEach( (item) -> item.removeListener(valueListener)); }); this.valueListener = valueListener; } /* What follows is all the required delegate methods */ @Override public int size() { return list.size(); } @Override public boolean isEmpty() { return list.isEmpty(); } @Override public boolean contains(Object o) { return list.contains(o); } @Override public Iterator iterator() { return list.iterator(); } @Override public Object[] toArray() { return list.toArray(); } @Override public  T[] toArray(T[] ts) { return list.toArray(ts); } @Override public boolean add(T e) { return list.add(e); } @Override public boolean remove(Object o) { return list.remove(o); } @Override public boolean containsAll(Collection clctn) { return list.containsAll(clctn); } @Override public boolean addAll(Collection clctn) { return list.addAll(clctn); } @Override public boolean addAll(int i, Collection clctn) { return list.addAll(i, clctn); } @Override public boolean removeAll(Collection clctn) { return list.removeAll(clctn); } @Override public boolean retainAll(Collection clctn) { return list.retainAll(clctn); } @Override public void replaceAll(UnaryOperator uo) { list.replaceAll(uo); } @Override public void sort(Comparator cmprtr) { list.sort(cmprtr); } @Override public void clear() { list.clear(); } @Override public T get(int i) { return list.get(i); } @Override public T set(int i, T e) { return list.set(i, e); } @Override public void add(int i, T e) { list.add(i, e); } @Override public T remove(int i) { return list.remove(i); } @Override public int indexOf(Object o) { return list.indexOf(o); } @Override public int lastIndexOf(Object o) { return list.lastIndexOf(o); } @Override public ListIterator listIterator() { return list.listIterator(); } @Override public ListIterator listIterator(int i) { return list.listIterator(i); } @Override public List subList(int i, int i1) { return list.subList(i, i1); } @Override public Spliterator spliterator() { return list.spliterator(); } @Override public void addListener(ListChangeListener ll) { list.addListener(ll); } @Override public void removeListener(ListChangeListener ll) { list.removeListener(ll); } @Override public boolean addAll(T... es) { return list.addAll(es); } @Override public boolean setAll(T... es) { return list.setAll(es); } @Override public boolean setAll(Collection clctn) { return list.setAll(clctn); } @Override public boolean removeAll(T... es) { return list.removeAll(es); } @Override public boolean retainAll(T... es) { return list.retainAll(es); } @Override public void remove(int i, int i1) { list.remove(i, i1); } @Override public FilteredList filtered(Predicate prdct) { return list.filtered(prdct); } @Override public SortedList sorted(Comparator cmprtr) { return list.sorted(cmprtr); } @Override public SortedList sorted() { return list.sorted(); } @Override public void addListener(InvalidationListener il) { list.addListener(il); } @Override public void removeListener(InvalidationListener il) { list.removeListener(il); } } 

以下代码显示了具有可观察值的可观察列表的简单实现:

 public class ObservableValueListWrapper> extends ObservableListWrapper { public ObservableValueListWrapper(List list) { super(list, o -> new Observable[] {o});}} 

或者您必须使用POJO创建列表:

 final ObservableList list = new ObservableListWrapper<>(new ArrayList(), o -> new Observable[] { new MyPOJOProperty(o) }); 

或者你使用它:

 final ObservableList list = new ObservableListWrapper<>(new ArrayList(), o -> { return new Observable[] { o.value1Property(), o.value2Property(), ...};}); 

这就对了! 谢谢。