Java 8: Observable List - Invalidation Listener nor Change Listener is called in case of property change

前端 未结 3 1913
说谎
说谎 2020-12-05 06:12

I build a custom property and add it to a observable list. But no listener is called if property content is changed. The following code snippets shows you the \'building\':<

相关标签:
3条回答
  • 2020-12-05 06:42

    The ObservableList isn't notifying the listeners whenever a property contained within the list is modified, it notifies when the list is notified.

    This can be seen when you modify your test:

    @Test
    public void testList() {
        final AtomicInteger counter = new AtomicInteger(0);
        final ObservableList<TestProperty> observableList = new ObservableListWrapper<>(new ArrayList<>());
    
        observableList.addListener(new ListChangeListener<TestProperty>() {
            @Override
            public void onChanged(Change<? extends TestProperty> 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());
    }
    

    EDIT: Added an example ObserverListener decorator which provides the auto registration/deregistration of the ObservableValue change listener as desired by the OP.

    /**
     * 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 <T>
     */
    public class ObservableValueList<T extends ObservableValue> implements ObservableList<T> {
    
        private final ObservableList<T> list;
        private final ChangeListener<T> valueListener;
    
        public ObservableValueList(ObservableList<T> list, ChangeListener<T> 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<? extends T> 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<T> iterator() {
            return list.iterator();
        }
    
        @Override
        public Object[] toArray() {
            return list.toArray();
        }
    
        @Override
        public <T> 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<? extends T> clctn) {
            return list.addAll(clctn);
        }
    
        @Override
        public boolean addAll(int i, Collection<? extends T> 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<T> uo) {
            list.replaceAll(uo);
        }
    
        @Override
        public void sort(Comparator<? super T> 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<T> listIterator() {
            return list.listIterator();
        }
    
        @Override
        public ListIterator<T> listIterator(int i) {
            return list.listIterator(i);
        }
    
        @Override
        public List<T> subList(int i, int i1) {
            return list.subList(i, i1);
        }
    
        @Override
        public Spliterator<T> spliterator() {
            return list.spliterator();
        }
    
        @Override
        public void addListener(ListChangeListener<? super T> ll) {
            list.addListener(ll);
        }
    
        @Override
        public void removeListener(ListChangeListener<? super T> 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<? extends T> 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<T> filtered(Predicate<T> prdct) {
            return list.filtered(prdct);
        }
    
        @Override
        public SortedList<T> sorted(Comparator<T> cmprtr) {
            return list.sorted(cmprtr);
        }
    
        @Override
        public SortedList<T> sorted() {
            return list.sorted();
        }
    
        @Override
        public void addListener(InvalidationListener il) {
            list.addListener(il);
        }
    
        @Override
        public void removeListener(InvalidationListener il) {
            list.removeListener(il);
        }
    
    }
    
    0 讨论(0)
  • 2020-12-05 06:44

    The following code shows a simple implementation for a observable list with observable values:

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

    Or you must create your list with a POJO:

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

    Or you use it so:

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

    That is it! Thanks.

    0 讨论(0)
  • 2020-12-05 06:47

    To create an observable list that will send "list updated" notifications if properties of elements of the list change, you need to create the list with an extractor. The extractor is a Callback that maps each element of the list to an array of Observables. If any of the Observables changes, InvalidationListeners and ListChangeListeners registered with the list will be notified.

    So in your testList() method, you can do

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

    If the title were able to change, and you also wanted the list to receive notifications when that happened, you could do that too:

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

    Note that because the extractor is a Callback (essentially a function), the implementation can be arbitrarily complex (observe one property conditionally based on the value of another, etc).

    0 讨论(0)
提交回复
热议问题