如何将属性绑定到列表属性中对象的属性?

时间:2016-11-02 12:34:43

标签: java javafx

我有这些课程:

class Parent {

    BooleanProperty total = new SimpleBooleanProperty();
    SimpleListProperty<Child> children = new SimpleListProperty<>(FXCollections.observableArrayList());
}

class Child {

    BooleanProperty single = new SimpleBooleanProperty();
}

我想要的是total当且仅当所有孩子的single都是假的时才会是假的。换句话说,当且仅当有一个single为真的孩子时,total为真。

我想出了这个绑定

total.bind(Bindings.createBooleanBinding(() -> {
    return children.stream().filter(c -> c.isSingle()).findAny().isPresent();
}, children.stream().map(c -> c.single).collect(Collectors.toCollection(FXCollections::observableArrayList))));

这是最好的方式吗?

我也应该只读total因为写入绑定属性会引发异常吗?

1 个答案:

答案 0 :(得分:7)

显式绑定到每个子级single属性的一个可能问题是,在调用total.bind(...)语句时会产生这些绑定。因此,如果新的Child对象随后添加到列表中,则不会观察到这些子single属性,如果它们发生更改,则绑定不会失效。同样,如果从列表中删除子项,则其single属性将不会被解除绑定。

作为替代方案,您可以使用extractor创建列表。请注意,对于大多数用例,您不需要ListProperty:只需直接使用ObservableList即可。所以你可以做到

ObservableList<Child> children = 
    FXCollections.observableArrayList(c -> new Observable[]{c.singleProperty()});

如果任何元素single属性无效(以及在列表中添加元素的时候),此列表现在将触发更新通知(并变为无效)。换句话说,列表本身会观察每个子项的single属性,如果有任何子项single,该列表将变为无效(导致绑定的任何内容无效)属性变得无效。该列表还确保在将子项添加到列表时观察新子项的single属性,并在从列表中删除子项时停止观察它们。

然后你需要

total.bind(Bindings.createBooleanBinding(() -> 
    children.stream().anyMatch(Child::isSingle), children);

最后,考虑使用ReadOnlyBooleanWrapper以便将属性公开为只读。这是整个事物的正确封装版本:

public class Parent {

    private ReadOnlyBooleanWrapper total = new ReadOnlyBooleanWrapper();
    private ObservableList<Child> children = 
        FXCollections.observableArrayList(c -> new Observable[] {c.singleProperty()});

    public Parent() {
        total.bind(Bindings.createBooleanBinding(() -> 
            children.stream().anyMatch(Child::isSingle), children);
    }

    public ReadOnlyBooleanProperty totalProperty() {
        return total.getReadOnlyProperty();
    }

    public ObservableList<Child> getChildren() {
        return children ;
    }

}

public class Child {
    private final BooleanProperty single = new SimpleBooleanProperty();

    public BooleanProperty singleProperty() {
        return single ;
    }

    public final boolean isSingle() {
        return singleProperty().get();
    }

    public final void setSingle(boolean single) {
        singleProperty().set(single);
    }
}