JavaFX将ObservableList由多个参数组成一个映射并绑定它们

时间:2017-02-24 12:03:03

标签: javafx-8

假设我们有一个ObservableList<Person>,其中Person有属性:年龄,性别。目标是创建一个类似于的可观察(在所有级别)地图 Map<Gender, Map<Age, List<Person>>>我可以绑定到ObservableList

这样做的原因是因为我必须根据这样的分组提取实时聚合数据。

1 个答案:

答案 0 :(得分:1)

这要求您为每个属性添加一个侦听器,并为属性指定bean,以避免为每个Person创建两个侦听器:

private final ObjectProperty<Gender> gender = new SimpleObjectProperty<>(this, "gender");
private final IntegerProperty age = new SimpleIntegerProperty(this, "age");
public static void main(String[] args) {
    ObservableList<Person> data = FXCollections.observableArrayList();
    ObservableMap<Gender, ObservableMap<Number, ObservableList<Person>>> grouped = FXCollections.observableHashMap();
    ChangeListener<Gender> genderChangeListener = (observable, oldValue, newValue) -> {
        ObservableMap<Number, ObservableList<Person>> m = grouped.get(oldValue);
        Person person = (Person) ((Property) observable).getBean();

        // remove person from list and remove list, if it becomes empty
        m.compute(person.getAge(), (a, lp) -> {
            lp.remove(person);
            return lp.isEmpty() ? null : lp;
        });

        // remove age map, if it's empty
        if (m.isEmpty()) {
            grouped.remove(oldValue);
        }

        // add person at new position generating the Map/List, if necessary
        grouped.computeIfAbsent(newValue, g -> FXCollections.observableHashMap())
                .computeIfAbsent(person.getAge(), a -> FXCollections.observableArrayList())
                .add(person);
    };
    ChangeListener<Number> ageChangeListener = (observable, oldValue, newValue) -> {
        Person person = (Person) ((Property) observable).getBean();
        ObservableMap<Number, ObservableList<Person>> map = grouped.get(person.getGender());

        // remove person from list and remove list, if it becomes empty
        map.compute(oldValue, (a, lp) -> {
            lp.remove(person);
            return lp.isEmpty() ? null : lp;
        });

        // add person at new position generating the List, if necessary
        map.computeIfAbsent(newValue, a -> FXCollections.observableArrayList())
                .add(person);
    };
    data.addListener((ListChangeListener.Change<? extends Person> c) -> {
        while (c.next()) {
            for (Person p : c.getRemoved()) {
                // unregister the listeners of removed Persons
                p.genderProperty().removeListener(genderChangeListener);
                p.ageProperty().removeListener(ageChangeListener);

                // remove person from grouped
                ObservableMap<Number, ObservableList<Person>> m = grouped.get(p.getGender());
                m.compute(p.getAge(), (a, lp) -> {
                    lp.remove(p);
                    return lp.isEmpty() ? null : lp;
                });
                if (m.isEmpty()) {
                    grouped.remove(p.getGender());
                }
            }
            for (Person p : c.getAddedSubList()) {
                // add listeners to person
                p.genderProperty().addListener(genderChangeListener);
                p.ageProperty().addListener(ageChangeListener);

                // add person to grouped generating the Map/List, if necessary
                grouped.computeIfAbsent(p.getGender(), g -> FXCollections.observableHashMap())
                        .computeIfAbsent(p.getAge(), a -> FXCollections.observableArrayList())
                        .add(p);
            }
        }
    });

    // test
    Person p = new Person("Frank", Gender.MALE, 20);
    Person p2 = new Person("Lisa", Gender.FEMALE, 32);
    Person p3 = new Person("Nora", Gender.FEMALE, 52);
    Person p4 = new Person("Carl", Gender.MALE, 62);

    System.out.println(grouped);
    data.addAll(p, p2, p3, p4);
    System.out.println(grouped);
    p3.setAge(32);
    System.out.println(grouped);
    p.setGender(Gender.FEMALE);
    System.out.println(grouped);
    data.remove(p3);
    System.out.println(grouped);
    data.remove(p4);
    System.out.println(grouped);
}