【问题标题】:Custom Observable List implementation, notifying consumers of changes自定义 Observable List 实现,通知消费者变化
【发布时间】:2015-03-10 19:43:58
【问题描述】:

我为 TreeItems 列表创建了一个自定义 ObservableList 实现。我的自定义实现可以从我的应用程序内部监听各种通知(使用 OSGi EventAdmin),并相应地更新自身。然后,我希望它的使用者(一个 TreeView 小部件)随着列表的更改而更新。但是,我看不到如何通知消费者。

在 ObservableList 子类中,我正在实现 addListener(ListChangeListener),我希望在将对象添加到小部件时调用它。但是它永远不会被调用;我没有听众,因此当列表更改时没有明显的方法来通知任何人。我一定是错过了什么。

这是我的 TreeItem 实现中的一个 sn-p,它返回我的 ObservableList 的一个实例以响应 getChildren 调用:

    @Override
    public ObservableList<TreeItem<DataObject>> getChildren() {
        if (needChildren) {
            needChildren = false;
            children = new MyObservableList();
        }
        return children;
    }

这是我的自定义 ObservableList 实现的精简版,它简单地包装了一个 FXCollections.observableArrayList 并添加了一个 OSGi 事件处理程序。我听取内部列表上的更改,以便将这些更改传递给我的听众。

    public class MyObservableList implements ObservableList<TreeItem<DataObject>>, EventHandler {
    private List<ListChangeListener<? super TreeItem<DataObject>>> changeListeners = new ArrayList<>();
    private List<InvalidationListener> invalidationListeners = new ArrayList<>();
    private ObservableList<TreeItem<DataObject>> theList;
    private int size;

    public MyObservableList() {
        theList = FXCollections.observableArrayList();

        theList.addListener(new ListChangeListener<TreeItem<DataObject>>() {
            @Override
            public void onChanged(Change<? extends TreeItem<DataObject>> change) {
                fireValueChangedEvent(change);
            }
        });
    }

    @Override
    public int size() {
        return theList.size();
    }

    @Override
    public boolean isEmpty() {
        return (size == 0);
    }

    @Override
    public boolean contains(Object o) {
        return theList.contains(o);
    }

    @Override
    public Iterator iterator() {
        return theList.iterator();
    }

    @Override
    public boolean remove(Object o) {
        return theList.remove(o);
    }

    @Override
    public boolean addAll(Collection c) {
        return theList.addAll(c);
    }

    @Override
    public boolean addAll(int index, Collection c) {
        return theList.addAll(index, c);
    }


    @Override
    public void clear() {
        theList.clear();
    }

    @Override
    public TreeItem<DataObject> get(int index) {
        return theList.get(index);
    }

    @Override
    public int indexOf(Object o) {
        return theList.indexOf(o);
    }

    @Override
    public int lastIndexOf(Object o) {
        return theList.lastIndexOf(o);
    }

    @Override
    public ListIterator listIterator() {
        return theList.listIterator();
    }

    @Override
    public ListIterator listIterator(int index) {
        return theList.listIterator(index);
    }


    @Override
    public List<TreeItem<DataObject>> subList(int fromIndex, int toIndex) {
        return theList.subList(fromIndex, toIndex);
    }

    @Override
    public Object[] toArray(Object[] a) {
        return theList.toArray(a);
    }

    @Override
    public void addListener(ListChangeListener<? super TreeItem<DataObject>> listChangeListener) {
        changeListeners.add(listChangeListener);
    }

    @Override
    public void removeListener(ListChangeListener<? super TreeItem<DataObject>> listChangeListener) {
        changeListeners.remove(listChangeListener);
    }

    @Override
    public boolean addAll(TreeItem<DataObject>... treeItems) {
        return theList.addAll(treeItems);
    }

    @Override
    public boolean setAll(TreeItem<DataObject>... treeItems) {
        return theList.setAll(treeItems);
    }

    @Override
    public boolean setAll(Collection<? extends TreeItem<DataObject>> treeItems) {
        return theList.setAll(treeItems);
    }

    @Override
    public boolean removeAll(TreeItem<DataObject>... treeItems) {
        return theList.removeAll(treeItems);
    }

    @Override
    public boolean retainAll(TreeItem<DataObject>... treeItems) {
        return theList.retainAll(treeItems);
    }

    @Override
    public void remove(int i, int i2) {
        theList.remove(i, i2);
    }

    @Override
    public Object[] toArray() {
        return theList.toArray();
    }

    @Override
    public boolean add(TreeItem<DataObject> dataObjectTreeItem) {
        return theList.add(dataObjectTreeItem);
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return theList.containsAll(c);
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return theList.removeAll(c);
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return theList.retainAll(c);
    }

    @Override
    public TreeItem<DataObject> set(int index, TreeItem<DataObject> element) {
        return theList.set(index, element);
    }

    @Override
    public void add(int index, TreeItem<DataObject> element) {
        theList.add(index, element);
    }

    @Override
    public TreeItem<DataObject> remove(int index) {
        return theList.remove(index);
    }

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

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

    private void fireValueChangedEvent(ListChangeListener.Change<? extends TreeItem<DataObject>> change) {
        for (ListChangeListener<? super TreeItem<DataObject>> listener : changeListeners) {
            listener.onChanged(change);
        }
    }

    @Override
    public void handleEvent(Event event) {
        // Here I add or remove TreeItem<DataObject> instances to the list based on event.
        //
        // At this point, onChanged() gets called above in my listener, but my changeListeners list is empty. There is
        // no one to pass the Change on to.
    }
}

感谢您的帮助。

【问题讨论】:

  • 只是为了确定,你确定你的TreeItem 不是叶子(即它的isLeaf() 方法不返回true)吗?此外,正在更新其子项的树项是否已扩展?最后,我看不出为什么要在自己的实现中包装可观察列表的充分理由。您也可以在 TreeItem 实现中处理事件并相应地修改子列表。
  • 是的,它绝对不是一片叶子。我已经弄清楚发生了什么,并会在几分钟内发布答案。
  • 哦,至于你的第二个问题,为什么要这样做——这是一个很好的问题。作为一般规则,我们试图将业务逻辑排除在 GUI 级别小部件之外,并且让我们的业务逻辑组件生成一个封装事件处理逻辑的 ObservableList 很有吸引力,因为它可以为更通用的解决方案提供可能性其他组件也是如此。但是根据我现在的想法,也许我需要考虑 BL 的 TreeItem 部分并将其移动到该组件中。

标签: javafx


【解决方案1】:

在查看 JavaFX 源代码后,我弄清楚了这里发生了什么。

事实证明,对孩子 ObservableList 的侦听都是在 TreeItem 本身中设置的,而不是像我以某种方式假设的那样设置在 TreeView 中。这意味着任何重写 getChildren() 的 TreeView 子类必须调用 super.getChildren() 并将其子类添加到结果列表中。这意味着无法使用自定义 ObservableList 实现,因为 TreeItem 被硬编码为使用 FXCollections.observableArrayList() 来创建列表。

我现在对此采取了不同的方法,我调用 super.getChildren(),添加我的孩子,然后实例化另一个对象,该对象包含对该列表的引用并执行我的应用程序的所有事件处理业务,在根据需要列出。所以我的 getChildren() 方法现在看起来像这样。

private MyEventHandler eventHandler;
private ObservableList<TreeItem<DataObject>> children;

@Override
public ObservableList<TreeItem<DataObject>> getChildren() {
    if (needChildren) {
        needChildren = false;
        children = super.getChildren();
        eventHandler = new MyEventHandler(children); // handles app events
    }
    return children;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多