Насколько я знаю, нет встроенного способа сделать это просто. Тем не менее, есть несколько способов сделать это. Наиболее (?) Эффективным, но более сложным способом было бы прослушивать ObservableSet
для добавления / удаления, наблюдать любые текущие DoubleProperty
элементы и изменять свойство total
самостоятельно.
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.beans.value.WeakChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener;
public class SomeClass {
private final ReadOnlyDoubleWrapper total = new ReadOnlyDoubleWrapper(this, "total");
private void setTotal(double total) { this.total.set(total); }
public final double getTotal() { return total.get(); }
public final ReadOnlyDoubleProperty totalProperty() { return total.getReadOnlyProperty(); }
private final ObservableSet<DoubleProperty> propertySet = FXCollections.observableSet();
private final ChangeListener<Number> elementListener = this::elementValueChanged;
private final WeakChangeListener<Number> weakElementListener =
new WeakChangeListener<>(elementListener);
public SomeClass() {
propertySet.addListener(this::propertySetChanged);
}
private void propertySetChanged(SetChangeListener.Change<? extends DoubleProperty> change) {
if (change.wasRemoved()) {
change.getElementRemoved().removeListener(weakElementListener);
setTotal(getTotal() - change.getElementRemoved().get());
}
if (change.wasAdded()) {
change.getElementAdded().addListener(weakElementListener);
setTotal(getTotal() + change.getElementAdded().get());
}
}
private void elementValueChanged(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue) {
setTotal(getTotal() - oldValue.doubleValue() + newValue.doubleValue());
}
}
Здесь SetChangeListener
, значение которого является ссылкой на метод propertySetChanged
, отслеживает любые изменения в ObservableSet
. Когда DoubleProperty
равно добавлено , оно добавляет значение указанного свойства к текущему итогу. Когда DoubleProperty
удаляется , удаляется , оно вычитает значение указанного свойства из текущей суммы. Этот слушатель также добавляет или удаляет ChangeListener
к или из DoubleProperty
, когда он добавляется или удаляется из ObservableSet
, соответственно.
ChangeListener
, значение которого является ссылкой на метод elementValueChanged
, обновляет свойство total
при изменении значения любого DoubleProperty
. Для этого сначала вычитают старое значение, а затем добавляют новое значение к текущему итогу. На самом деле это WeakChangeListener
, который оборачивает оригинал ChangeListener
, который добавляется или удаляется. Это помогает избежать потенциальных утечек памяти. Не забывайте сохранять четкую ссылку на оригинал ChangeListener
при использовании WeakChangeListener
, в противном случае оригинал ChangeListener
может быть собран слишком рано.
Второй вариант - перестраивать привязку каждый раз, когда ObservableSet
становится недействительным, а затем привязывать свойство total
к указанной привязке.
import javafx.beans.Observable;
import javafx.beans.binding.DoubleExpression;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
public class SomeClass {
private final ReadOnlyDoubleWrapper total = new ReadOnlyDoubleWrapper(this, "total");
private void setTotal(double total) { this.total.set(total); }
public final double getTotal() { return total.get(); }
public final ReadOnlyDoubleProperty totalProperty() { return total.getReadOnlyProperty(); }
private final ObservableSet<DoubleProperty> propertySet = FXCollections.observableSet();
public SomeClass() {
propertySet.addListener(this::propertySetInvalidated);
}
private void propertySetInvalidated(Observable observable) {
if (propertySet.isEmpty()) {
total.unbind();
setTotal(0.0);
} else if (propertySet.size() == 1) {
total.bind(propertySet.iterator().next());
} else {
DoubleExpression sum = null;
for (DoubleProperty property : propertySet) {
sum = (sum != null) ? sum.add(property) : property;
}
total.bind(sum);
}
}
}
В этом случае мы добавляем InvalidationListener
к ObservableSet
. Этот слушатель будет вызываться всякий раз, когда элемент (ы) добавляется или удаляется из ObservableSet
. Когда это произойдет, произойдет 1 из 3 вещей:
- Если
ObservableSet
теперь пусто, отсоедините свойство total
и установите его в ноль.
- Это особый случай, когда нет элементов
- Если
ObservableSet
теперь содержит только один элемент, просто привяжите свойство total
к указанному элементу.
- Еще один особый случай, касающийся одного элемента. Это мешает нам создавать ненужные объекты и предварительно выполнять ненужные вычисления, которые произойдут, если мы просто перейдем к третьей ветви.
- В противном случае создайте одну большую привязку, которая вычисляет сумму, а затем привяжите
total
к этой привязке.
- В этой ветке используется
DoubleExpression.add(ObservableNumberValue)
. Результирующий DoubleBinding
из этого вызова метода будет обновляться всякий раз, когда изменяется одна из двух наблюдаемых. Это сводится к одному DoubleExpression
, к которому мы затем привязываем свойство total
.
Этот второй вариант будет менее эффективен, поскольку требует каждый раз повторять весь ObservableSet
. Это также потенциально приводит к созданию множества DoubleBinding
объектов. Однако вам может оказаться проще кодировать / понимать, и снижение производительности может оказаться недостаточно значительным для вашего приложения.