JavaFX: получить промежуточный итог значений из ObservableSet объекта DoubleProperty - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть ObservableSet<DoubleProperty> itemSet , который содержит DoubleProperty prop1 для любого количества экземпляров Item.

Я хочу создать еще один DoubleProperty всего , который будет отражать текущую сумму всех DoubleProperty в itemSet .

Двойное значение каждого DoubleProperty в наборе может изменяться независимо. Значение total должно отражать эти изменения.

Это Item класс:

class Item {
    DoubleProperty prop1;
    DoubleProperty prop2;

    public Item() {
        this.prop1 = new SimpleDoubleProperty(1.0);
        this.prop2 = new SimpleDoubleProperty(2.0);

        itemSet.add(this.prop1);
    }
}

Это своего рода класс глобальной переменной ...

class ItemValue {
    private ItemValue itemValue = null;

    ObservableSet<DoubleProperty> itemSet = FXCollections.observableSet();
    DoubleProperty total;

    private ItemValue() {
        this.total = new SimpleDoubleProperty(0.0);

        // create several Item's here...

        itemSet.addListener((InvalidationListener) observable -> {
        /*
        Something which binds the total
        I figure it will need to go here so that if new items get added the total will reflect that?
        */
        });
    }

    public ItemValue get() {
        if (itemValue == null) itemValue = new ItemValue();
        return itemValue;
}

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Насколько я знаю, нет встроенного способа сделать это просто. Тем не менее, есть несколько способов сделать это. Наиболее (?) Эффективным, но более сложным способом было бы прослушивать 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 вещей:

  1. Если ObservableSet теперь пусто, отсоедините свойство total и установите его в ноль.
    • Это особый случай, когда нет элементов
  2. Если ObservableSet теперь содержит только один элемент, просто привяжите свойство total к указанному элементу.
    • Еще один особый случай, касающийся одного элемента. Это мешает нам создавать ненужные объекты и предварительно выполнять ненужные вычисления, которые произойдут, если мы просто перейдем к третьей ветви.
  3. В противном случае создайте одну большую привязку, которая вычисляет сумму, а затем привяжите total к этой привязке.
    • В этой ветке используется DoubleExpression.add(ObservableNumberValue). Результирующий DoubleBinding из этого вызова метода будет обновляться всякий раз, когда изменяется одна из двух наблюдаемых. Это сводится к одному DoubleExpression, к которому мы затем привязываем свойство total.

Этот второй вариант будет менее эффективен, поскольку требует каждый раз повторять весь ObservableSet. Это также потенциально приводит к созданию множества DoubleBinding объектов. Однако вам может оказаться проще кодировать / понимать, и снижение производительности может оказаться недостаточно значительным для вашего приложения.

0 голосов
/ 05 сентября 2018

Я бы дал itemSet прослушиватель изменений и просто пересчитывал итог с помощью какого-либо метода при каждом его вызове.

См. https://stackoverflow.com/a/44141262/8729420.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...