Crferreira の回答では、Fluent API を使用してバインディングのチェーンを構築しています。オブジェクトを削除または置換できる場合、クリーニングと保守が難しいことがわかりました。低レベル API を使用することをお勧めします。
JavaFX API には事前に作成されたバインディングの膨大なセットがありますが、ListBinding は、その要素の 1 つが新しいプロパティ値を取得しても無効になりません。したがって、リストの変更をリッスンし、新しいプロパティ自体に再バインドする 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);
}
}
リスト内の要素のプロパティの合計を監視するような一般的なタスクに、醜いボイラープレートが必要になるのは悲しいことです。