ListChangeListener wasPermutated块

时间:2014-09-06 23:56:36

标签: java javafx javafx-8 observablelist

ListChangeListener的JavaDoc提供了一个用于处理更改的模板。但是,我不知道如何处理排列。对于每个索引,我都可以找到项目的新索引所在的位置,但我不知道如何处理它。这是一个独立于编程语言的难题。 ObservableList只能添加(),remove(),set(),还有一个迭代器。

如果我有一个原始列表[1,2,3],并将list []绑定到它,则绑定列表[1,2,3]需要匹配它。 如果原始列表更换了比较器以便原始列表现在显示为[3,2,1],那么如何使绑定列表跟随?

/**
 * Binds a source list's elements to a destination list. Any changes made in
 * the source list will reflect in the destination list.
 *
 * @param <SRC> The source list's object type.
 * @param <DEST> The destination list's object type.
 * @param dest The destination list that will be bound to the src list.
 * @param src The source list to watch for changes, and propagate up to the
 * destination list.
 * @param transformer A function that will transform a source list data
 * type, A, into a destination list data type, B.
 */
public static <SRC, DEST> void bindLists(
        ObservableList<DEST> dest, ObservableList<SRC> src, Function<? super SRC, ? extends DEST> transformer) {
    /*Add the initial data into the destination list.*/
    for (SRC a : src) {
        dest.add(transformer.apply(a));
    }
    /*Watch for future data to add to the destination list. Also watch for removal
     of data form the source list to remove its respective item in the destination
     list.*/
    src.addListener((ListChangeListener.Change<? extends SRC> c) -> {
        while (c.next()) {
            if (c.wasPermutated()) {
                /*How do you handle permutations? Do you remove and then add, 
                 or add and then remove, or use set, or use a copy arraylist 
                 and set the right indices? Removing/adding causes concurrent modifications.*/
                for (int oldIndex = c.getFrom(); oldIndex < c.getTo(); oldIndex++) {
                    int newIndex = c.getPermutation(oldIndex);
                    dest.remove(oldIndex);
                    dest.add(newIndex, dest.get(oldIndex));
                }
            } else if (c.wasUpdated()) {

            } else {
                /*Respond to removed data.*/
                for (SRC item : c.getRemoved()) {
                    int from = c.getFrom();
                    dest.remove(from);
                }
                /*Respond to added data.*/
                for (SRC item : c.getAddedSubList()) {
                    int indexAdded = src.indexOf(item);
                    dest.add(indexAdded, transformer.apply(item));
                }
            }
        }
    });
}

2 个答案:

答案 0 :(得分:6)

对于排列情况,我不打算尝试使用add()remove()来处理它。这将导致索引转移并使事情混乱(至少对我而言)。

从概念上讲,你得到的是一系列受影响的元素,以及一个包含一些数字的数组,这些数字表示每个元素的移动位置。我想你明白了。在您的代码中,

    newIndex = getPermutation(oldIndex);

表示位于oldIndex的元素需要移至newIndex。皱纹是,如果你直接进行移动,你可能会覆盖尚未移动的元素。我认为解决这个问题的最简单方法是制作受影响的子范围的副本,然后单步执行排列数组并将副本中的元素移动到新的位置。执行此操作的代码是:

    int from = c.getFrom();
    int to = c.getTo();
    List<DEST> copy = new ArrayList<>(dest.subList(from, to));
    for (int oldIndex = from; oldIndex < to; oldIndex++) {
        int newIndex = c.getPermutation(oldIndex);
        dest.set(newIndex, copy.get(oldIndex - from));
    }

这是一种排列,因此每个元素都会在某处结束,而不会添加或删除任何元素。这意味着您不必复制列表范围,并且可以在仅使用单个临时空间元素的同时在移动链之后一次移动一个元素。可能存在多个链循环,因此您必须检测并处理它。这听起来很复杂。我会把它留给另一个回答者。 :-)对于我的钱,复制受影响的范围简单易懂。

正常列表操作不会触发排列和更新的更改模式。如果查看javafx.collections.ObservableListBase,您可以看到列表实现可用于建立特定更改信息的协议。如果实现向nextPermutationnextUpdate方法提供正确的信息,它将触发这些更多其他更改模式。我不确定在JavaFX中可能触发它们的是什么。例如,更改节点堆叠顺序的Node.toFront()Node.toBack()方法可能会生成排列更改,但它们似乎不会。我不知道任何可能导致更新更改的内容。

从语义上讲,我认为更新更改意味着列表范围内的元素已更改,但列表的长度保持不变。这与“替换”改变模式形成对比,其中一系列元素可能被不同数量的元素替换。也可能是更新更改意味着元素本身没有被替换 - 也就是说,列表内容没有改变 - 只是元素的内部状态已经改变。

答案 1 :(得分:2)

请注意,您在此处执行的操作是在EasyBind中实现的。了解如何mapbind列表。

基本上,这是您实施bindLists方法的方式:

public static <SRC, DEST> void bindLists(
        ObservableList<DEST> dest, ObservableList<SRC> src,
        Function<? super SRC, ? extends DEST> transformer) {

    EasyBind.listBind(dest, EasyBind.map(src, transformer));
}