2015-09-12 3 views
0

Я пытаюсь иметь свойство total, которое получается путем умножения двух свойств вместе, а именно currentPrice и volumeHeld, где currentPrice фактически полученный путем загрузки Google профинансирует цена акций каждые 10 секунд. И он автоматически обновляется каждые 10 секунд.JavaFX: Наручники не работает, как ожидалось

Теперь getCurrentPrice() инициализирован в 0, как показано в коде. Через 10 секунд он получает новое значение, и все это прекрасно работает.

Но в приведенном ниже методе связывания total не обновляется автоматически при изменении свойства currentPrice.

totalBinding = Bindings.createDoubleBinding(() -> { 
     System.out.println("current price: " + getCurrentPrice() + "vol held: " + getVolumeHeld()); 
     return getCurrentPrice() * getVolumeHeld(); 
    }); 

    total.bind(totalBinding); 

Вопрос: я обнаружил, что в createDoubleBinding заявлении выше, getCurrentPrice() имеет значение 0 (как упоминалось выше), и когда его значение изменяется, то это изменение не распространяется в total собственности. Под этим я подразумеваю, что свойство total не может получить новое значение от getCurrentPrice(), даже когда текущая цена изменилась.

Так что проблема два раза, но я предполагаю, что решения для обоих моих вопросов ниже будет похож, если не точно такой же:

  1. Как я могу решить эту проблему, упомянутых выше?

  2. Позже, я буду связывание этого total имущества другой собственности отработать в общей сложности на total собственностей для всех Trade объектов). Это терпит неудачу и всегда равно 0. Этот метод написан в другом классе, то есть не в классе Trade.

UPDATE:

код ниже:

class SummaryofTrade{ 
    ...  
    sumOfTotals = new ReadOnlyDoubleWrapper(); 
    sumOfTotalsBinding = Bindings.createDoubleBinding(() -> { 
     double sum = 0; 
     for(Trade t : this.observableListOfTrades){ 
      sum += t.getTotal(); 
     } 
     return sum;   
    }, total);  // I cannot put "total" as a second parameter, as it is a property that resides in the Trade class , not this class. 
    sumOfTotals.bind(sumOfTotalsBinding); 
    ... 
} 

сообщение журнала ошибок:

Caused by: java.lang.Error: Unresolved compilation problem: 
    total cannot be resolved to a variable 

Пожалуйста, обратите внимание, что sumOfTotalsBinding и sumOfTotals живут в другом классе ,

Код для торговли объекта ниже:

class Trade{ 
     ... 
     private final ReadOnlyDoubleWrapper total; 
     private final ReadOnlyDoubleWrapper currentPrice; 
     private DoubleProperty volumeHeld; 
     public DoubleBinding totalBinding; 



     private final ScheduledService<Number> priceService = new ScheduledService<Number>() { 
     @Override 
     public Task<Number> createTask(){ 
      return new Task<Number>() { 
       @Override 
       public Number call() throws InterruptedException, IOException { 
        return getCurrentPriceFromGoogle(); 
       } 
      }; 
     } 
     }; 

    public Trade(){ 
     ... 
     priceService.setPeriod(Duration.seconds(10)); 
     priceService.setOnFailed(e -> priceService.getException().printStackTrace()); 
     this.currentPrice = new ReadOnlyDoubleWrapper(0); 
     this.currentPrice.bind(priceService.lastValueProperty()); 
     startMonitoring(); 
     this.total   = new ReadOnlyDoubleWrapper(); 
     DoubleBinding totalBinding = Bindings.createDoubleBinding(() -> 
      getCurrentPrice() * getVolumeHeld(), 
      currentPriceProperty(), volumeHeldProperty());     
     total.bind(totalBinding); 
    } 


     // volume held 
    public double getVolumeHeld(){ 
     return this.volumeHeld.get(); 
    } 

    public DoubleProperty volumeHeldProperty(){ 
     return this.volumeHeld; 
    } 

    public void setVolumeHeld(double volumeHeld){ 
     this.volumeHeld.set(volumeHeld); 
    } 

     // multi-threading 
    public final void startMonitoring() { 
     priceService.restart(); 
    } 

    public final void stopMonitoring() { 
     priceService.cancel(); 
    } 

     public ReadOnlyDoubleProperty currentPriceProperty(){ 
     return this.currentPrice.getReadOnlyProperty(); 
    } 

    public final double getCurrentPrice(){ 
     return currentPriceProperty().get(); 
    } 

     // total 
    public final Double getTotal(){ 
     return totalProperty().getValue(); 
    } 

    public ReadOnlyDoubleProperty totalProperty(){ 
     return this.total; 
    } 
} 

UPDATE 9/15/2015:

Я пытаюсь разработать мою проблему в логическом порядке здесь. Дайте мне знать, если это не имеет смысла. Благодарю.

Во-первых, в Trade class выше (пожалуйста, обратите внимание, что приведенный выше код был обновлен и указано свойство зависимостей), каждый объект торговли содержит total свойство, которое является продуктом currentPrice и VolumeHeld.Если пользователь вручную отредактирует значения текущей цены и объема. Свойство total будет обновляться автоматически.

Теперь у меня есть Объект ObservableList of Trade, каждый из которых имеет свойство total. Моя цель состоит в том, чтобы суммировать свойство total каждого объекта Trade в наблюдаемом списке и привязать сумму к переменной с именем sumOfTotals. Это делается в классе под названием SummaryOfTrade. И всякий раз, когда свойство total любого из торгов в списке наблюдаемых изменяется, свойство sumOfTotals также должно автоматически меняться.

class SummaryofTrade{ 
    ...  
    // within constructor, we have 
    sumOfTotals = new ReadOnlyDoubleWrapper(); 
    sumOfTotalsBinding = Bindings.createDoubleBinding(() -> { 
     double sum = 0; 
     for(Trade t : this.observableListOfTrades){ 
      sum += t.getTotal(); 
     } 
     return sum;   
    }, totalProperty());  
    sumOfTotals.bind(sumOfTotalsBinding); 
    ... 
} 

Здесь возникает проблема. Затмение говорит, что он не признает свойство объекта Trade,, totalProperty. Сообщение об ошибке, показанное ниже.

сообщение журнала ошибок:

Caused by: java.lang.Error: Unresolved compilation problem: 
    The method totalProperty() is undefined for the type SummaryOfTrade 

Я уточнял зависимость свойств уже пока Eclipse, бросает ошибку. Как мне это разрешить?

+0

Это не совсем понятно, почему ответ от @AlmasB не отвечает на этот вопрос (в частности, к какому классу свойства определяются не делает никакой разницы, как вы напишите привязку). Значения вашей привязки остались на нуле, потому что вы не указали зависимости в привязке.Если вы исправите это, как в этом ответе, он должен теперь работать. Возможно, вы можете обновить с помощью [MCVE], чтобы показать, каковы остальные проблемы? –

+0

Сверху я указал второй параметр 'total' в' createDoubleBinding', т. Е. Зависимости в привязке, но eclipse не распознает его. Ошибка заключается в том, что 'total property' не живет в классе' SummaryOfTrade', поэтому я не мог ссылаться на него. Если это все еще запутывает, я снова обновлю его сегодня вечером. – mynameisJEFF

+0

@James_D, пожалуйста, просмотрите обновление и сообщите мне, если это не имеет смысла. Спасибо – mynameisJEFF

ответ

1

У вас есть ObservableList<Trade>, где каждый объект Trade имеет наблюдаемый totalProperty(). Ваш sumOfTotals необходимо обновить, когда изменится содержимое этого списка или когда какое-либо из отдельных totalProperty() s, принадлежащих к любому из элементов, изменится.

Вы можете сделать это вручную:

DoubleBinding sumOfTotalsBinding = new DoubleBinding() { 

    { 
     bind(observableListOfTrades); 
     observableListOfTrades.forEach(trade -> bind(trade.totalProperty()); 
     observableListOfTrades.addListener((Change<? extends Trade> change) -> { 
      while (change.next()) { 
       if (change.wasAdded()) { 
        change.getAddedSubList().forEach(trade -> bind(trade.totalProperty())); 
       } 
       if (change.wasRemoved()) { 
        change.getRemoved().forEach(trade -> unbind(trade.totalProperty())); 
       } 
      } 
     }); 
    } 

    @Override 
    public double computeValue() { 
     return observableListOfTrades.stream().collect(Collectors.summingDouble(Trade::getTotal)); 
    } 
}; 

Или, вы можете создать свой список с extractor. Это приведет список Стреляет обновления уведомлений (тем самым маркируя его недействительным), когда какой-либо из указанных свойств, относящихся к элементам изменить:

ObservableList<Trade> observableListOfTrades = 
    FXCollections.observableArrayList(trade -> new Observable[] { trade.totalProperty() }); 

, а затем вы можете просто сделать

sumOfTotals.bind(Bindings.createDoubleBinding(() -> 
    observableListOfTrades.stream().collect(Collectors.summingDouble(Trade::getTotal)), 
    observableListOfTrades); 

так как теперь привязка только к observableListOfTrades приведет к перерасчету при изменении любого из индивидуальных итогов.

Вот SSCCE:

import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 
import java.util.Random; 
import java.util.function.Function; 
import java.util.stream.Collectors; 

import javafx.application.Application; 
import javafx.beans.Observable; 
import javafx.beans.binding.Bindings; 
import javafx.beans.binding.DoubleBinding; 
import javafx.beans.property.DoubleProperty; 
import javafx.beans.property.IntegerProperty; 
import javafx.beans.property.ReadOnlyDoubleProperty; 
import javafx.beans.property.ReadOnlyDoubleWrapper; 
import javafx.beans.property.ReadOnlyStringWrapper; 
import javafx.beans.property.SimpleDoubleProperty; 
import javafx.beans.property.SimpleIntegerProperty; 
import javafx.beans.value.ObservableValue; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.geometry.HPos; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.Label; 
import javafx.scene.control.TableColumn; 
import javafx.scene.control.TableView; 
import javafx.scene.control.TextField; 
import javafx.scene.control.cell.TextFieldTableCell; 
import javafx.scene.layout.BorderPane; 
import javafx.scene.layout.ColumnConstraints; 
import javafx.scene.layout.GridPane; 
import javafx.scene.layout.HBox; 
import javafx.scene.layout.Priority; 
import javafx.stage.Stage; 
import javafx.util.converter.DoubleStringConverter; 
import javafx.util.converter.IntegerStringConverter; 

public class TradeTableExample extends Application { 

    @Override 
    public void start(Stage primaryStage) { 
     TableView<Trade> table = new TableView<>(); 
     table.setEditable(true); 
     TableColumn<Trade, String> nameCol = column("Name", trade -> new ReadOnlyStringWrapper(trade.getName())); 
     TableColumn<Trade, Integer> volumeCol = column("Volume", t -> t.volumeProperty().asObject()); 
     TableColumn<Trade, Double> priceCol = column("Price", t -> t.priceProperty().asObject()); 
     TableColumn<Trade, Number> totalCol = column("Total", Trade::totalProperty); 

     volumeCol.setCellFactory(TextFieldTableCell.forTableColumn(new IntegerStringConverter())); 
     priceCol.setCellFactory(TextFieldTableCell.forTableColumn(new DoubleStringConverter())); 

     table.getColumns().addAll(Arrays.asList(nameCol, volumeCol, priceCol, totalCol)); 

     ObservableList<Trade> data = FXCollections.observableArrayList(trade -> new Observable[] {trade.totalProperty()}); 

     DoubleBinding grandTotal = Bindings.createDoubleBinding(() -> 
      data.stream().collect(Collectors.summingDouble(Trade::getTotal)), 
      data); 

     data.addAll(createData()); 
     table.setItems(data); 

     Label totalLabel = new Label(); 
     totalLabel.textProperty().bind(grandTotal.asString("Total: %,.2f")); 

     TextField nameField = new TextField(); 
     TextField volumeField = new TextField("0"); 
     TextField priceField = new TextField("0.00"); 

     Button add = new Button("Add"); 
     add.setOnAction(e -> { 
      data.add(
       new Trade(nameField.getText(), 
         Integer.parseInt(volumeField.getText()), 
         Double.parseDouble(priceField.getText()))); 
      nameField.setText(""); 
      volumeField.setText("0"); 
      priceField.setText("0.00"); 
     }); 

     Button delete = new Button("Delete"); 
     delete.setOnAction(e -> data.remove(table.getSelectionModel().getSelectedIndex())); 
     delete.disableProperty().bind(table.getSelectionModel().selectedItemProperty().isNull()); 

     HBox buttons = new HBox(5, add, delete); 

     GridPane controls = new GridPane(); 
     controls.addRow(0, new Label("Name:"), nameField); 
     controls.addRow(1, new Label("Volume:"), volumeField); 
     controls.addRow(2, new Label("Price:"), priceField); 
     controls.add(buttons, 0, 3, 2, 1); 
     controls.add(totalLabel, 0, 4, 2, 1); 

     ColumnConstraints leftCol = new ColumnConstraints(); 
     leftCol.setHalignment(HPos.RIGHT); 
     ColumnConstraints rightCol = new ColumnConstraints(); 
     rightCol.setHgrow(Priority.ALWAYS); 

     controls.getColumnConstraints().addAll(leftCol, rightCol); 

     GridPane.setHalignment(controls, HPos.LEFT); 
     GridPane.setHalignment(totalLabel, HPos.LEFT); 

     controls.setHgap(5); 
     controls.setVgap(5); 

     BorderPane root = new BorderPane(table, null, null, controls, null); 
     Scene scene = new Scene(root, 600, 600); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 

    private List<Trade> createData() { 
     Random rng = new Random(); 
     List<Trade> trades = new ArrayList<>(); 
     for (int i=0; i<10; i++) { 
      StringBuilder name = new StringBuilder(); 
      for (int c = 0; c < 3; c++) { 
       name.append(Character.toString((char)(rng.nextInt(26)+'A'))); 
      } 
      double price = rng.nextInt(100000)/100.0 ; 
      int volume = rng.nextInt(10000); 
      trades.add(new Trade(name.toString(), volume, price)); 
     } 
     return trades ; 
    } 

    private <S,T> TableColumn<S,T> column(String text, Function<S, ObservableValue<T>> property) { 
     TableColumn<S,T> col = new TableColumn<>(text); 
     col.setCellValueFactory(cellData -> property.apply(cellData.getValue())); 
     return col ; 
    } 

    public static class Trade { 
     private final String name ; 
     private final IntegerProperty volume = new SimpleIntegerProperty(); 
     private final DoubleProperty price = new SimpleDoubleProperty(); 
     private final ReadOnlyDoubleWrapper total = new ReadOnlyDoubleWrapper(); 

     public Trade(String name, int volume, double price) { 
      this.name = name ; 
      setPrice(price); 
      setVolume(volume); 
      total.bind(priceProperty().multiply(volumeProperty())); 
     } 

     public final String getName() { 
      return name ; 
     } 

     public final IntegerProperty volumeProperty() { 
      return this.volume; 
     } 

     public final int getVolume() { 
      return this.volumeProperty().get(); 
     } 

     public final void setVolume(final int volume) { 
      this.volumeProperty().set(volume); 
     } 

     public final DoubleProperty priceProperty() { 
      return this.price; 
     } 

     public final double getPrice() { 
      return this.priceProperty().get(); 
     } 

     public final void setPrice(final double price) { 
      this.priceProperty().set(price); 
     } 

     public final ReadOnlyDoubleProperty totalProperty() { 
      return this.total.getReadOnlyProperty(); 
     } 

     public final double getTotal() { 
      return this.totalProperty().get(); 
     } 


    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
} 
+0

Первый предложенный вами метод работы. Не знаю, почему второй предложенный вами метод не работает, так как мне удалось скомпилировать и запустить код, но 'sumOfTotals' всегда возвращает 0. – mynameisJEFF

+0

Отлично работает для меня. См. Обновление. –

+0

Интересно. Я узнаю, где моя ошибка. Благодарю. – mynameisJEFF

5

Так как текущая цена и объем провел это свойство, можно просто связать их непосредственно:

total.bind(currentPriceProperty().multiply(volumeHeldProperty())); 

Если вам абсолютно необходимо использовать пользовательскую двойную привязку, в первую очередь необходимо обеспечить зависимости, так что вычисление выполняется один раз в зависимости становятся аннулированных согласно documentation:

DoubleBinding totalBinding = new DoubleBinding() { 

    { 
     super.bind(currentPrice, volumeHeld); 
    } 

    @Override 
    protected double computeValue() { 
     return currentPrice.get() * volumeHeld.get(); 
    } 
}; 

следующая вспомогательная функция обеспечивает Bindings также должны работать:

DoubleBinding totalBinding = Bindings.createDoubleBinding(() -> 
     currentPrice.get() * volumeHeld.get(), 
     currentPrice, volumeHeld); 
+0

поэтому 'currentPrice.get()' работает, но метод экземпляра 'getCurrentPrice()', который я написал, не работает? – mynameisJEFF

+0

Я добавил подробности о второй части моего вопроса, объясняя, как это не удается. – mynameisJEFF

+0

Обратите внимание, что в ответе при использовании 'Bindings.createDoubleBinding (...)' дополнительные параметры предоставляются методу, указывая свойства, которые необходимо соблюдать. (Связывание должно знать, что нужно наблюдать, чтобы знать, когда нужно обновить себя.) –

Смежные вопросы