在JavaFX ObservableArrayList中查找已更改的对象

时间:2016-02-19 00:21:57

标签: object javafx listener observable

在我的Observablelist中的对象的一个​​属性上设置一个监听器并为其添加一个监听器花了我太长时间。

 ObservableList<Track> observableResult = FXCollections.observableArrayList((Track tr)-> new Observable[]{tr.selectedProperty()});
                    observableResult.addListener(new ListChangeListener<Track>() {
                        @Override
                        public void onChanged(Change<? extends Track> c) {
                            c.next();
                            for(Track k : c.getAddedSubList()){
                                System.out.println(k.getTrackName());
                            }
                        }
                    });

但我似乎无法找到所做的更改的实际对象。 Change类似乎只支持添加和删除的成员,这些成员不会被其中的实际更改触发。

我有一个解决方法,只是调用另一个循环通过整个ObservableArrayList的方法,例如,只获取选定的项目,但是在我有几千个对象后,这会非常昂贵。查找已更改的源成员将允许我将它们推送到另一个阵列并节省大量开销。

1 个答案:

答案 0 :(得分:0)

您可以在更改时调用getFrom()以获取已更改项目的索引。我认为没有办法真正弄清楚哪个属性发生了变化(如果你在提取器中列出了多个属性)或获得旧值,但也许这就足够了。

如果你需要更多,你可以考虑将自己的听众注册到要跟踪的列表,这将是棘手的但并非不可能。

这是SSCCE演示getFrom()电话:

import java.util.Random;
import java.util.stream.IntStream;

import javafx.beans.Observable;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;

public class ListChangeListenerTest {

    public static void main(String[] args) {
        ObservableList<Item> itemList = FXCollections.observableArrayList(item -> new Observable[]{item.valueProperty()});
        itemList.addListener((Change<? extends Item> c) -> {
            while (c.next()) {
                if (c.wasUpdated()) {
                    int index = c.getFrom();
                    System.out.println("Updated item at "+index+" new value is "+itemList.get(index).getValue());
                }
            }
        });
        IntStream.rangeClosed(1, 1000).mapToObj(Item::new).forEach(itemList::add);

        Random rng = new Random();
        itemList.get(rng.nextInt(itemList.size())).setValue(rng.nextInt(10000));

    }

    public static class Item {
        private final IntegerProperty value = new SimpleIntegerProperty();
        public Item(int value) {
            setValue(value);
        }
        public final IntegerProperty valueProperty() {
            return this.value;
        }

        public final int getValue() {
            return this.valueProperty().get();
        }

        public final void setValue(final int value) {
            this.valueProperty().set(value);
        }



    }

}

这是一个手动管理属性上的侦听器的版本。注意

  1. 这不会在列表中使用提取器
  2. 构造Item bean中的属性,传递对拥有该属性的bean的引用。这允许属性上的监听器获得对Item的引用(通过一些丑陋的向下转换)
  3. 这提供了更多的灵活性;例如如果您想检查多个属性的修改并执行不同的操作,这将允许这样做。如您所见,侦听器也可以访问旧值。

    import java.util.Random;
    import java.util.stream.IntStream;
    
    import javafx.beans.property.IntegerProperty;
    import javafx.beans.property.Property;
    import javafx.beans.property.SimpleIntegerProperty;
    import javafx.beans.value.ChangeListener;
    import javafx.collections.FXCollections;
    import javafx.collections.ListChangeListener.Change;
    import javafx.collections.ObservableList;
    
    public class ListChangeListenerTest {
    
        public static void main(String[] args) {    
    
            ChangeListener<Number> valueListener = (obs, oldValue, newValue) -> {
                Item item = (Item) ((Property<?>) obs).getBean();
                System.out.println("Value for "+item+" changed from " + oldValue + " to "+newValue);
            };
    
            ObservableList<Item> itemList = FXCollections.observableArrayList();
            itemList.addListener((Change<? extends Item> change) -> {
                while (change.next()) {
                    if (change.wasAdded()) {
                        for (Item item : change.getAddedSubList()) {
                            item.valueProperty().addListener(valueListener);
                        }
                    }
                    if (change.wasRemoved()) {
                        for (Item item : change.getRemoved()) {
                            item.valueProperty().removeListener(valueListener);
                        }
                    }
                }
            });
    
            IntStream.rangeClosed(1, 1000).mapToObj(Item::new).forEach(itemList::add);
    
            Random rng = new Random();
            itemList.get(rng.nextInt(itemList.size())).setValue(rng.nextInt(10000));
    
        }
    
        public static class Item {
            private final IntegerProperty value = new SimpleIntegerProperty(this, "value");
            private final String id ;
            public Item(int value) {
                id = "Item "+value ;
                setValue(value);
            }
            public final IntegerProperty valueProperty() {
                return this.value;
            }
    
            public final int getValue() {
                return this.valueProperty().get();
            }
    
            public final void setValue(final int value) {
                this.valueProperty().set(value);
            }
    
            @Override
            public String toString() {
                return id ;
            }
    
        }
    
    }
    

    最后,如果你想要考虑&#34;批量&#34;更新,您需要自己实施ObservableList。您可以通过继承ModifiableObservableListBase来完成此操作,基本思路非常简单。由于必须创建代表更新的Change对象,实现变得有点乏味,但它并不太糟糕。这是一个允许更新连续范围的示例:

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    import java.util.function.Consumer;
    
    import javafx.collections.ListChangeListener.Change;
    import javafx.collections.ModifiableObservableListBase;
    
    public class UpdatingObservableList<T> extends ModifiableObservableListBase<T> {
    
        private final List<T> list ;
    
        public UpdatingObservableList(List<T> list) {
            this.list = list ;
        }
    
        public UpdatingObservableList() {
            this(new ArrayList<>());
        }
    
        public void updateSublist(int start, int end, Consumer<T> updater) {
            if (start < 0) throw new ArrayIndexOutOfBoundsException("Start ("+start+") cannot be < 0");
            if (end < start) throw new IllegalArgumentException("End ("+end+") cannot be less than start ("+start+")");
            if (end > size()) throw new ArrayIndexOutOfBoundsException("End ("+end+") cannot be greater than list size ("+size()+")");
    
            for (T element : list.subList(start, end)) {
                updater.accept(element);
            }
    
            fireChange(createUpdate(start, end));
        }
    
        @Override
        public T get(int index) {
            return list.get(index);
        }
    
        @Override
        public int size() {
            return list.size();
        }
    
        @Override
        protected void doAdd(int index, T element) {
            list.add(index, element);
        }
    
        @Override
        protected T doSet(int index, T element) {
            return list.set(index, element);
        }
    
        @Override
        protected T doRemove(int index) {
            return list.remove(index);
        }
    
        private Change<T> createUpdate(int start, int end) {
            return new Change<T>(this) {
    
                private boolean initialState = true ;
    
                @Override
                public boolean next() {
                    if (initialState) {
                        initialState = false ;
                        return true ;
                    }
                    return false ;
                }
    
                @Override
                public void reset() {
                    initialState = true ;
                }
    
                @Override
                public int getFrom() {
                    checkState();
                    return start ;
                }
    
                @Override
                public int getTo() {
                    checkState();
                    return end ;
                }
    
                @Override
                public List<T> getAddedSubList() {
                    checkState();
                    return Collections.emptyList();
                }
    
                @Override
                public List<T> getRemoved() {
                    checkState();
                    return Collections.emptyList();
                }
    
                @Override
                protected int[] getPermutation() {
                    checkState();
                    return new int[0];
                }
    
                @Override
                public boolean wasAdded() {
                    checkState();
                    return false ;
                }
    
                @Override
                public boolean wasRemoved() {
                    checkState();
                    return false ;
                }
    
                @Override
                public boolean wasUpdated() {
                    return true ;
                }
    
                @Override
                public boolean wasPermutated() {
                    checkState();
                    return false ;
                }
    
                @Override
                public int getRemovedSize() {
                    checkState();
                    return 0 ;
                }
    
                @Override
                public int getAddedSize() {
                    checkState();
                    return 0 ;
                }
    
                private void checkState() {
                    if (initialState) {
                        throw new IllegalStateException("Must call Change.next()");
                    }
                }
    
            };
        }
    }
    

    这里是使用它的测试类的一个版本。请注意,更新是通过列表执行的:

    import java.util.Random;
    import java.util.stream.IntStream;
    
    import javafx.beans.property.IntegerProperty;
    import javafx.beans.property.SimpleIntegerProperty;
    import javafx.collections.ListChangeListener.Change;
    
    public class ListChangeListenerTest {
    
        public static void main(String[] args) {    
    
            UpdatingObservableList<Item> itemList = new UpdatingObservableList<Item>();
            itemList.addListener((Change<? extends Item> change) -> {
                while (change.next()) {
                    if (change.wasUpdated()) {
                        for (int i = change.getFrom() ; i < change.getTo() ; i++) {
                            System.out.println(itemList.get(i) + " updated - new value: "+itemList.get(i).getValue());
                        }
                    }
                }
            });
    
            IntStream.rangeClosed(1, 1000).mapToObj(Item::new).forEach(itemList::add);
    
            Random rng = new Random();
            int start = rng.nextInt(itemList.size());
            int end = Math.min(itemList.size(), start + 1 + rng.nextInt(15));
            itemList.updateSublist(start, end, item -> item.setValue(rng.nextInt(10000)));
        }
    
        public static class Item {
            private final IntegerProperty value = new SimpleIntegerProperty(this, "value");
            private final String id ;
            public Item(int value) {
                id = "Item "+value ;
                setValue(value);
            }
            public final IntegerProperty valueProperty() {
                return this.value;
            }
    
            public final int getValue() {
                return this.valueProperty().get();
            }
    
            public final void setValue(final int value) {
                this.valueProperty().set(value);
            }
    
            @Override
            public String toString() {
                return id ;
            }
    
        }
    
    }