Crferreira 的答案使用 Fluent API 来构建绑定链,我发现当可以删除或替换对象时很难清理和维护。最好使用 Low-Level API。
尽管 JavaFX API 中有大量预构建的绑定内容,但 ListBinding 在其中一个元素获得新属性值时不会失效。所以你必须创建你的 IntegerBinding 子类,它监听列表中的变化并重新绑定到新属性本身。
基于来自类似答案的代码。
import java.util.ArrayList;
import java.util.List;
import javafx.beans.binding.IntegerBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
class RootElement {
ObservableList<SimpleElement> elements = FXCollections.observableList(new ArrayList<SimpleElement>());
IntegerBinding totalWeight;
public RootElement() {
totalWeight = new SumOfWeightsForListOfSimpleElementsIntegerBinding(elements);
}
public void addElement(SimpleElement element) {
elements.add(element);
}
public void removeElement(SimpleElement element) {
elements.remove(element);
}
public Integer getWeigth() {
return totalWeight.getValue();
}
}
class SimpleElement {
IntegerProperty weight;
public SimpleElement() {
this(0);
}
public SimpleElement(Integer weight) {
this.weight = new SimpleIntegerProperty(weight);
}
public int getWeight() {
return weight.get();
}
public void setWeight(int weight) {
this.weight.set(weight);
}
public IntegerProperty weightProperty() {
return weight;
}
}
class SumOfWeightsForListOfSimpleElementsIntegerBinding extends IntegerBinding {
// Reference to our observable list
private final ObservableList<SimpleElement> boundList;
// Array of currently observed properties of elements of our list
private IntegerProperty[] observedProperties = {};
// Listener that has to call rebinding in response of any change in observable list
private final ListChangeListener<SimpleElement> BOUND_LIST_CHANGE_LISTENER
= (ListChangeListener.Change<? extends SimpleElement> change) -> {
refreshBinding();
};
SumOfWeightsForListOfSimpleElementsIntegerBinding(ObservableList<SimpleElement> boundList) {
this.boundList = boundList;
boundList.addListener(BOUND_LIST_CHANGE_LISTENER);
refreshBinding();
}
@Override
protected int computeValue() {
int i = 0;
for (IntegerProperty bp : observedProperties) {
i += bp.get();
}
return i;
}
@Override
public void dispose() {
boundList.removeListener(BOUND_LIST_CHANGE_LISTENER);
unbind(observedProperties);
}
private void refreshBinding() {
// Clean old properties from IntegerBinding's inner listener
unbind(observedProperties);
// Load new properties
List<IntegerProperty> tmplist = new ArrayList<>();
boundList.stream().map((boundList1) -> boundList1.weightProperty()).forEach((integerProperty) -> {
tmplist.add(integerProperty);
});
observedProperties = tmplist.toArray(new IntegerProperty[0]);
// Bind IntegerBinding's inner listener to all new properties
super.bind(observedProperties);
// Invalidate binding to generate events
// Eager/Lazy recalc depends on type of listeners attached to this instance
// see IntegerBinding sources
this.invalidate();
}
}
public class Main {
public static void main(String[] args) {
SimpleElement se1 = new SimpleElement(10);
SimpleElement se2 = new SimpleElement(20);
SimpleElement se3 = new SimpleElement(30);
RootElement root = new RootElement();
root.totalWeight.addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> {
System.out.println(newValue);
});
root.addElement(se1);
root.addElement(se2);
root.addElement(se3);
se1.setWeight(1000);
root.removeElement(se3);
}
}
可悲的是,监视列表中元素属性的总和这样的常见任务需要丑陋的样板。