如何在JavaFX中连接可观察列表?

时间:2016-05-30 11:34:00

标签: java list javafx concat observablelist

通过连接,我的意思是获取一个新列表,该列表侦听所有连接部分的更改。

方法FXCollections#concat(ObservableList<E>... lists)的目的是什么?如果它只是合并了几个列表,那么我认为没有任何意义可以为此设置单独的方法。

如果考虑做我想做的事情那么它就行不通了:

import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

public class ConcatObservabeList {

   public static void main(String[] args) {

      ObservableList<Integer> list1 = FXCollections.observableArrayList();
      ObservableList<Integer> list2 = FXCollections.observableArrayList();


      ObservableList<Integer> concat = FXCollections.concat(list1, list2);
      concat.addListener(new ListChangeListener<Integer>() {
         public void onChanged(Change<? extends Integer> c) {
            System.out.println("changed");
         }
      });

      list1.add(12);


   }
}

6 个答案:

答案 0 :(得分:6)

我同样需要将几个ObservableLists连接/聚合到JavaFX LineChart的一个列表中。我在此处或在另一个答案中发布的github上找到的示例始终将每个更改中的子列表中的所有条目复制到聚合列表中。对于包含许多条目的列表,这似乎不太优雅。

我决定实现我的on版本,该版本跟踪聚合列表中子列表的位置,当子列表中的元素发生更改时,在聚合列表中应用相同的更改。仍然有改进的余地(不是使用委托列表,而是直接扩展ObservableList,或者将子事件中的事件发送到聚合列表并覆盖getter和迭代器 - 这将有所帮助)但是我想我发布了版本在这里,也许它可以帮助某人。

代码:

/**
 * This class aggregates several other Observed Lists (sublists), observes changes on those sublists and applies those same changes to the
 * aggregated list.
 * Inspired by:
 * - http://stackoverflow.com/questions/25705847/listchangelistener-waspermutated-block
 * - http://stackoverflow.com/questions/37524662/how-to-concatenate-observable-lists-in-javafx
 * - https://github.com/lestard/advanced-bindings/blob/master/src/main/java/eu/lestard/advanced_bindings/api/CollectionBindings.java
 * Posted result on: http://stackoverflow.com/questions/37524662/how-to-concatenate-observable-lists-in-javafx
 */
public class AggregatedObservableArrayList<T> {

    protected final List<ObservableList<T>> lists = new ArrayList<>();
    final private List<Integer> sizes = new ArrayList<>();
    final private List<InternalListModificationListener> listeners = new ArrayList<>();
    final protected ObservableList<T> aggregatedList = FXCollections.observableArrayList();

    public AggregatedObservableArrayList() {

    }

    /**
     * The Aggregated Observable List. This list is unmodifiable, because sorting this list would mess up the entire bookkeeping we do here.
     *
     * @return an unmodifiable view of the aggregatedList
     */
    public ObservableList<T> getAggregatedList() {
        return FXCollections.unmodifiableObservableList(aggregatedList);
    }

    public void appendList(@NotNull ObservableList<T> list) {
        assert !lists.contains(list) : "List is already contained: " + list;
        lists.add(list);
        final InternalListModificationListener listener = new InternalListModificationListener(list);
        list.addListener(listener);
        //System.out.println("list = " + list + " puttingInMap=" + list.hashCode());
        sizes.add(list.size());
        aggregatedList.addAll(list);
        listeners.add(listener);
        assert lists.size() == sizes.size() && lists.size() == listeners.size() :
              "lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size() + " or not equal to listeners.size=" + listeners.size();
    }

    public void prependList(@NotNull ObservableList<T> list) {
        assert !lists.contains(list) : "List is already contained: " + list;
        lists.add(0, list);
        final InternalListModificationListener listener = new InternalListModificationListener(list);
        list.addListener(listener);
        //System.out.println("list = " + list + " puttingInMap=" + list.hashCode());
        sizes.add(0, list.size());
        aggregatedList.addAll(0, list);
        listeners.add(0, listener);
        assert lists.size() == sizes.size() && lists.size() == listeners.size() :
              "lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size() + " or not equal to listeners.size=" + listeners.size();
    }

    public void removeList(@NotNull ObservableList<T> list) {
        assert lists.size() == sizes.size() && lists.size() == listeners.size() :
              "lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size() + " or not equal to listeners.size=" + listeners.size();
        final int index = lists.indexOf(list);
        if (index < 0) {
            throw new IllegalArgumentException("Cannot remove a list that is not contained: " + list + " lists=" + lists);
        }
        final int startIndex = getStartIndex(list);
        final int endIndex = getEndIndex(list, startIndex);
        // we want to find the start index of this list inside the aggregated List. End index will be start + size - 1.
        lists.remove(list);
        sizes.remove(index);
        final InternalListModificationListener listener = listeners.remove(index);
        list.removeListener(listener);
        aggregatedList.remove(startIndex, endIndex + 1); // end + 1 because end is exclusive
        assert lists.size() == sizes.size() && lists.size() == listeners.size() :
              "lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size() + " or not equal to listeners.size=" + listeners.size();
    }

    /**
     * Get the start index of this list inside the aggregated List.
     * This is a private function. we can safely asume, that the list is in the map.
     *
     * @param list the list in question
     * @return the start index of this list in the aggregated List
     */
    private int getStartIndex(@NotNull ObservableList<T> list) {
        int startIndex = 0;
        //System.out.println("=== searching startIndex of " + list);
        assert lists.size() == sizes.size() : "lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size();
        final int listIndex = lists.indexOf(list);
        for (int i = 0; i < listIndex; i++) {
            final Integer size = sizes.get(i);
            startIndex += size;
            //System.out.println(" startIndex = " + startIndex + " added=" + size);
        }
        //System.out.println("startIndex = " + startIndex);
        return startIndex;
    }

    /**
     * Get the end index of this list inside the aggregated List.
     * This is a private function. we can safely asume, that the list is in the map.
     *
     * @param list       the list in question
     * @param startIndex the start of the list (retrieve with {@link #getStartIndex(ObservableList)}
     * @return the end index of this list in the aggregated List
     */
    private int getEndIndex(@NotNull ObservableList<T> list, int startIndex) {
        assert lists.size() == sizes.size() : "lists.size=" + lists.size() + " not equal to sizes.size=" + sizes.size();
        final int index = lists.indexOf(list);
        return startIndex + sizes.get(index) - 1;
    }

    private class InternalListModificationListener implements ListChangeListener<T> {

        @NotNull
        private final ObservableList<T> list;

        public InternalListModificationListener(@NotNull ObservableList<T> list) {
            this.list = list;
        }

        /**
         * Called after a change has been made to an ObservableList.
         *
         * @param change an object representing the change that was done
         * @see Change
         */
        @Override
        public void onChanged(Change<? extends T> change) {
            final ObservableList<? extends T> changedList = change.getList();
            final int startIndex = getStartIndex(list);
            final int index = lists.indexOf(list);
            final int newSize = changedList.size();
            //System.out.println("onChanged for list=" + list + " aggregate=" + aggregatedList);
            while (change.next()) {
                final int from = change.getFrom();
                final int to = change.getTo();
                //System.out.println(" startIndex=" + startIndex + " from=" + from + " to=" + to);
                if (change.wasPermutated()) {
                    final ArrayList<T> copy = new ArrayList<>(aggregatedList.subList(startIndex + from, startIndex + to));
                    //System.out.println("  permutating sublist=" + copy);
                    for (int oldIndex = from; oldIndex < to; oldIndex++) {
                        int newIndex = change.getPermutation(oldIndex);
                        copy.set(newIndex - from, aggregatedList.get(startIndex + oldIndex));
                    }
                    //System.out.println("  permutating done sublist=" + copy);
                    aggregatedList.subList(startIndex + from, startIndex + to).clear();
                    aggregatedList.addAll(startIndex + from, copy);
                } else if (change.wasUpdated()) {
                    // do nothing
                } else {
                    if (change.wasRemoved()) {
                        List<? extends T> removed = change.getRemoved();
                        //System.out.println("  removed= " + removed);
                        // IMPORTANT! FROM == TO when removing items.
                        aggregatedList.remove(startIndex + from, startIndex + from + removed.size());
                    }
                    if (change.wasAdded()) {
                        List<? extends T> added = change.getAddedSubList();
                        //System.out.println("  added= " + added);
                        //add those elements to your data
                        aggregatedList.addAll(startIndex + from, added);
                    }
                }
            }
            // update the size of the list in the map
            //System.out.println("list = " + list + " puttingInMap=" + list.hashCode());
            sizes.set(index, newSize);
            //System.out.println("listSizesMap = " + sizes);
        }

    }

    public String dump(Function<T, Object> function) {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        aggregatedList.forEach(el -> sb.append(function.apply(el)).append(","));
        final int length = sb.length();
        sb.replace(length - 1, length, "");
        sb.append("]");
        return sb.toString();
    }
}

jUnit测试:

/**
 * Testing the AggregatedObservableArrayList
 */
public class AggregatedObservableArrayListTest {


    @Test
    public void testObservableValue() {
        final AggregatedObservableArrayList<IntegerProperty> aggregatedWrapper = new AggregatedObservableArrayList<>();
        final ObservableList<IntegerProperty> aggregatedList = aggregatedWrapper.getAggregatedList();
        aggregatedList.addListener((Observable observable) -> {
            System.out.println("observable = " + observable);
        });

        final ObservableList<IntegerProperty> list1 = FXCollections.observableArrayList();
        final ObservableList<IntegerProperty> list2 = FXCollections.observableArrayList();
        final ObservableList<IntegerProperty> list3 = FXCollections.observableArrayList();

        list1.addAll(new SimpleIntegerProperty(1), new SimpleIntegerProperty(2), new SimpleIntegerProperty(3), new SimpleIntegerProperty(4),
                     new SimpleIntegerProperty(5));
        list2.addAll(new SimpleIntegerProperty(10), new SimpleIntegerProperty(11), new SimpleIntegerProperty(12), new SimpleIntegerProperty(13),
                     new SimpleIntegerProperty(14), new SimpleIntegerProperty(15));
        list3.addAll(new SimpleIntegerProperty(100), new SimpleIntegerProperty(110), new SimpleIntegerProperty(120), new SimpleIntegerProperty(130),
                     new SimpleIntegerProperty(140), new SimpleIntegerProperty(150));

        // adding list 1 to aggregate
        aggregatedWrapper.appendList(list1);
        assertEquals("wrong content", "[1,2,3,4,5]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // removing elems from list1
        list1.remove(2, 4);
        assertEquals("wrong content", "[1,2,5]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // adding second List
        aggregatedWrapper.appendList(list2);
        assertEquals("wrong content", "[1,2,5,10,11,12,13,14,15]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // removing elems from second List
        list2.remove(1, 3);
        assertEquals("wrong content", "[1,2,5,10,13,14,15]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // replacing element in first list
        list1.set(1, new SimpleIntegerProperty(3));
        assertEquals("wrong content", "[1,3,5,10,13,14,15]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // adding third List
        aggregatedWrapper.appendList(list3);
        assertEquals("wrong content", "[1,3,5,10,13,14,15,100,110,120,130,140,150]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // emptying second list
        list2.clear();
        assertEquals("wrong content", "[1,3,5,100,110,120,130,140,150]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // adding new elements to second list
        list2.addAll(new SimpleIntegerProperty(203), new SimpleIntegerProperty(202), new SimpleIntegerProperty(201));
        assertEquals("wrong content", "[1,3,5,203,202,201,100,110,120,130,140,150]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // sorting list2. this results in permutation
        list2.sort((o1, o2) -> o1.getValue().compareTo(o2.getValue()));
        assertEquals("wrong content", "[1,3,5,201,202,203,100,110,120,130,140,150]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // removing list2 completely
        aggregatedWrapper.removeList(list2);
        assertEquals("wrong content", "[1,3,5,100,110,120,130,140,150]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // updating one integer value in list 3
        SimpleIntegerProperty integer = (SimpleIntegerProperty) list3.get(0);
        integer.set(1);
        assertEquals("wrong content", "[1,3,5,1,110,120,130,140,150]", aggregatedWrapper.dump(ObservableIntegerValue::get));

        // prepending list 2 again
        aggregatedWrapper.prependList(list2);
        assertEquals("wrong content", "[201,202,203,1,3,5,1,110,120,130,140,150]", aggregatedWrapper.dump(ObservableIntegerValue::get));

    }

}

答案 1 :(得分:2)

添加到ListChangeListenerObservableList会将某些特定changes视为整个列表。如API所引用here所示,也可以听取任何祖先列表的开销很大。由于FXCollections.concat()只是将源列表中的引用复制到目标的后备列表,因此添加到concat 的侦听器将查看对concat所做的更改;它会看到对list1list2的更改。

如果您因某些其他原因不需要创建新的ObservableList汇总列表的方式允许您为每个列表添加相同的侦听器。< / p>

控制台:

changed { [42] added at 0 }

代码:

import java.util.ArrayList;
import java.util.List;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

/**
 * @see https://stackoverflow.com/a/37527245/230513
 */
public class ObservableListAggregate {

    public static void main(String[] args) {
        ObservableList<Integer> list1 = FXCollections.observableArrayList();
        ObservableList<Integer> list2 = FXCollections.observableArrayList();
        Aggregate<ObservableList<Integer>> aggregate = new Aggregate(list1, list2);
        aggregate.addListener(new ListChangeListener<ObservableList<Integer>>() {
            @Override
            public void onChanged(ListChangeListener.Change<? extends ObservableList<Integer>> c) {
                System.out.println("changed " + c);
            }
        });
        list1.add(42);
    }

    private static class Aggregate<T> {

        List<ObservableList<T>> lists = new ArrayList<>();

        public Aggregate(ObservableList<T>... lists) {
            for (ObservableList<T> list : lists) {
                this.lists.add(list);
            }
        }

        public final void addListener(ListChangeListener<? super T> listener) {
            for (ObservableList<T> list : lists) {
                list.addListener(listener);
            }
        }

        public final void removeListener(ListChangeListener<? super T> listener) {
            for (ObservableList<T> list : lists) {
                list.removeListener(listener);
            }
        }
    }
}

要查看各个列表元素的更改,请使用ObservableList<Observable>,例如ObservableList<IntegerProperty>。在下面的示例中,请注意,添加到ip的{​​{1}}是list1中稍后修改的相同 IntegerProperty

控制台:

concat

代码:

IntegerProperty [value: 42]
concat changed { [IntegerProperty [value: 2147483647]] added at 1 }

答案 2 :(得分:1)

我偶然发现了相同的问题,因为方法FXCollections.concat(...)并没有随着源列表的更改而改变,这是我的用例所必需的。

由于这个问题的其他答案对我来说似乎有点过大,所以我将添加自己的解决方案,它有两个主要限制:

  • 生成的ObservableList将对源列表的内容只读。
  • 生成的ObservableList将是每次更改的新实例,因此,基于它们的可能的UI元素将在每次更改时重新创建。

由于任何ListBinding都会计算ObservableList作为其值并同时实现(因此有一个)ObservableList,因此可以轻松地使用它来增强默认值{{1 }}方法来更新源列表的更改:

concat

答案 3 :(得分:0)

我遇到了同样的问题,并在库advanced-bindingsjavadoc of the helper method)中为此用例创建了一个帮助器。

答案 4 :(得分:0)

我也有同样的问题。最后我列出了ObservableList:

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.WeakInvalidationListener;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.WeakListChangeListener;

/**
 * Read only view on a list of {@link ObservableList}
 *
 * @author Marcel Heckel
 */
public class CompositeObservableList<T> extends AbstractList<T> implements ObservableList<T>
{
    protected List<ObservableList<T>>             lists;

    protected List<ListChangeListener<? super T>> listChangeListeners          = new ArrayList<>();

    protected List<InvalidationListener>          invalidationListeners        = new ArrayList<>();

    protected InvalidationListener                internalInvalidationListener = this::invalidated;

    protected ListChangeListener<T>               internalListChangeListener   = this::onChanged;

    protected WeakInvalidationListener            weakInvalidationListener     = new WeakInvalidationListener(
        internalInvalidationListener);

    protected WeakListChangeListener<T>           weakListChangeListener       = new WeakListChangeListener<>(
        internalListChangeListener);

    public CompositeObservableList()
    {
        this.lists = new ArrayList<>();
    }

    public CompositeObservableList(List<ObservableList<T>> lists)
    {
        this.lists = lists;

        for (ObservableList<T> l : lists)
        {
            l.addListener(weakInvalidationListener);
            l.addListener(weakListChangeListener);
        }
    }

    public void addObservableList(ObservableList<T> l)
    {
        lists.add(l);
        l.addListener(weakInvalidationListener);
        l.addListener(weakListChangeListener);
    }

    /** remove listeners and clears the internal list */
    public void clearLists()
    {
        for (ObservableList<T> l : lists)
        {
            l.removeListener(weakInvalidationListener);
            l.removeListener(weakListChangeListener);
        }
        lists.clear();
    }

    ///////////////////////////////////////
    // listeners

    private void invalidated(Observable observable)
    {
        for (InvalidationListener l : invalidationListeners)
            l.invalidated(CompositeObservableList.this);
    }

    private void onChanged(ListChangeListener.Change<? extends T> c)
    {
        int idx = getStartIndexOfListReference(c.getList());
        assert (idx >= 0);
        if (idx < 0)
            return;

        c = new IndexOffsetChange<>(CompositeObservableList.this, idx, c);

        for (ListChangeListener<? super T> l : listChangeListeners)
        {
            l.onChanged(c);
        }
    }

    private int getStartIndexOfListReference(ObservableList<?> l)
    {
        int startIndex = 0;
        for (int i = 0; i < lists.size(); i++ )
        {
            if (l == lists.get(i))
                return startIndex;
            startIndex += l.size();
        }
        return -1;
    }

    ////////////////////////////////////////

    @Override
    public int size()
    {
        int size = 0;
        for (Collection<T> c : lists)
            size += c.size();
        return size;
    }

    @Override
    public boolean isEmpty()
    {
        for (Collection<T> c : lists)
            if ( !c.isEmpty())
                return false;
        return true;
    }

    @Override
    public boolean contains(Object obj)
    {
        for (Collection<T> c : lists)
            if (c.contains(obj))
                return true;
        return false;
    }

    @Override
    public boolean containsAll(Collection<?> c)
    {
        for (Object ele : c)
        {
            if ( !this.contains(ele))
                return false;
        }
        return true;
    }

    @Override
    public int indexOf(Object o)
    {
        int index = 0;
        for (List<T> l : lists)
        {
            int i = l.indexOf(o);
            if (i >= 0)
                return index + i;
            index += l.size();
        }
        return -1;
    }

    @Override
    public T get(int index)
    {
        if (index < 0)
            throw new IndexOutOfBoundsException("index: " + index + " - size: " + size());

        for (List<T> l : lists)
        {
            if (l.size() > index)
                return l.get(index);
            index -= l.size();
        }
        throw new IndexOutOfBoundsException("index: " + index + " - size: " + size());
    }

    @Override
    public Iterator<T> iterator()
    {
        return new Iterator<T>()
            {
                Iterator<T>                 currentIterator = null;

                Iterator<ObservableList<T>> listsIterator   = lists.iterator();

                @Override
                public boolean hasNext()
                {
                    while (true)
                    {
                        if (currentIterator != null && currentIterator.hasNext())
                            return true;
                        if ( !listsIterator.hasNext())
                            return false;
                        currentIterator = listsIterator.next().iterator();
                    }
                }

                @Override
                public T next()
                {
                    if ( !hasNext())
                        throw new NoSuchElementException();
                    return currentIterator.next();
                }

                @Override
                public void remove()
                {
                    throw new UnsupportedOperationException();
                }

            };
    }

    // editing methods

    @Override
    public boolean add(T obj)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean remove(Object obj)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean addAll(Collection<? extends T> c)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean removeAll(Collection<?> c)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean retainAll(final Collection<?> c)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public void clear()
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean addAll(int index, Collection<? extends T> c)
    {

        throw new UnsupportedOperationException();
    }

    @Override
    public T set(int index, T element)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public void add(int index, T element)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public T remove(int index)
    {
        throw new UnsupportedOperationException();
    }

    // editing methods of ObservableList list

    @Override
    public boolean addAll(T... elements)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean setAll(T... elements)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean setAll(Collection<? extends T> c)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean removeAll(T... elements)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean retainAll(T... elements)
    {
        throw new UnsupportedOperationException();
    }

    @Override
    public void remove(int from, int to)
    {
        throw new UnsupportedOperationException();
    }

    /////////////////////////////

    @Override
    public void addListener(InvalidationListener listener)
    {
        invalidationListeners.add(listener);
    }

    @Override
    public void removeListener(InvalidationListener listener)
    {
        invalidationListeners.remove(listener);
    }

    @Override
    public void addListener(ListChangeListener<? super T> listener)
    {
        listChangeListeners.add(listener);

    }

    @Override
    public void removeListener(ListChangeListener<? super T> listener)
    {
        listChangeListeners.remove(listener);
    }

    ////////////////

    private static class IndexOffsetChange<T> extends ListChangeListener.Change<T>
    {
        private final int                                    indexOffset;

        private final ListChangeListener.Change<? extends T> delegate;

        public IndexOffsetChange(ObservableList<T> list, final int indexOffset,
            ListChangeListener.Change<? extends T> c)
        {
            super(list);
            this.indexOffset = indexOffset;
            this.delegate = c;
        }

        @Override
        public boolean next()
        {
            return delegate.next();
        }

        @Override
        public void reset()
        {
            delegate.reset();
        }

        @Override
        public int getFrom()
        {
            return delegate.getFrom() + indexOffset;
        }

        @Override
        public int getTo()
        {
            return delegate.getTo() + indexOffset;
        }

        @Override
        public boolean wasPermutated()
        {
            return delegate.wasPermutated();
        }

        @Override
        public int getPermutation(int i)
        {
            return indexOffset + super.getPermutation(i - indexOffset);
        }

        @Override
        protected int[] getPermutation()
        {
            return null;
        }

        @SuppressWarnings("unchecked")
        @Override
        public List<T> getAddedSubList()
        {
            return (List<T>) delegate.getAddedSubList();
        }

        @Override
        public int getAddedSize()
        {
            return delegate.getAddedSize();
        }

        @Override
        public boolean wasAdded()
        {
            return delegate.wasAdded();
        }

        @SuppressWarnings("unchecked")
        @Override
        public List<T> getRemoved()
        {
            return (List<T>) delegate.getRemoved();
        }

        @Override
        public int getRemovedSize()
        {
            return delegate.getRemovedSize();
        }

        @Override
        public boolean wasRemoved()
        {
            return delegate.wasRemoved();
        }

        @Override
        public boolean wasReplaced()
        {
            return delegate.wasReplaced();
        }

        @Override
        public boolean wasUpdated()
        {
            return delegate.wasUpdated();
        }

    }

}

答案 5 :(得分:0)

如果您满意使用库,建议使用EasyBind

ObservableList<String> listA = ...;
ObservableList<String> listB = ...;
ObservableList<String> combinedList = EasyBind.concat(listA, listB);

如果您不喜欢添加新的依赖项,那么使用ListBinding的@LukasKörfer的答案将是最好的。

相关问题