javafx:绑定未按预期工作
我试图得到一个属性javafx:绑定未按预期工作,java,javafx,Java,Javafx,我试图得到一个属性total,它是通过将两个属性相乘得到的,即currentPrice和volumeHeld,其中currentPrice实际上是通过每10秒下载一次谷歌金融股票价格获得的。它每10秒自动更新一次 现在,getCurrentPrice()被初始化为0,如代码所示。10秒后,它拾取一个新值,所有这些都正常工作 但是在下面的绑定方法中,total不会在currentPrice属性更改时自动更新 totalBinding = Bindings.createDoubleBinding((
total
,它是通过将两个属性相乘得到的,即currentPrice
和volumeHeld
,其中currentPrice
实际上是通过每10秒下载一次谷歌金融股票价格获得的。它每10秒自动更新一次
现在,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()
中提取新值
totalBinding = Bindings.createDoubleBinding(() -> {
System.out.println("current price: " + getCurrentPrice() + "vol held: " + getVolumeHeld());
return getCurrentPrice() * getVolumeHeld();
});
total.bind(totalBinding);
因此,问题有两个方面,但我猜下面两个问题的解决方案即使不完全相同,也会相似:
total
属性绑定到另一个属性,以计算出所有Trade
对象的total
属性的总数。这失败得很惨,它总是等于0此方法在不同的类中编写,即不在交易类中。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
Caused by: java.lang.Error: Unresolved compilation problem:
The method totalProperty() is undefined for the type SummaryOfTrade
请注意,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;
}
}
这就是问题所在。Eclipse表示它无法识别交易对象的属性,totalProperty
。错误消息如下所示
错误日志消息:
Caused by: java.lang.Error: Unresolved compilation problem:
total cannot be resolved to a variable
Caused by: java.lang.Error: Unresolved compilation problem:
The method totalProperty() is undefined for the type SummaryOfTrade
我已经指定了属性依赖项,但是Eclipse抛出了一个错误。如何解决此问题?由于当前价格和持有量都是属性,您可以直接绑定它们:
total.bind(currentPriceProperty().multiply(volumeHeldProperty()));
如果您确实需要使用自定义双重绑定,则首先需要提供依赖项,以便在依赖项失效后根据以下条件执行计算:
绑定
提供的以下帮助函数也应该可以工作:
DoubleBinding totalBinding = Bindings.createDoubleBinding(() ->
currentPrice.get() * volumeHeld.get(),
currentPrice, volumeHeld);
由于当前价格和持有量都是属性,您可以直接绑定它们:
total.bind(currentPriceProperty().multiply(volumeHeldProperty()));
如果您确实需要使用自定义双重绑定,则首先需要提供依赖项,以便在依赖项失效后根据以下条件执行计算:
绑定
提供的以下帮助函数也应该可以工作:
DoubleBinding totalBinding = Bindings.createDoubleBinding(() ->
currentPrice.get() * volumeHeld.get(),
currentPrice, volumeHeld);
您有一个
可观察列表
,其中每个交易
对象都有一个可观察的totalProperty()。当列表的内容更改时,或者当属于任何元素的任何单个totalProperty()
s更改时,需要更新您的sumOfTotals
您可以手动执行此操作:
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));
}
};
由于现在仅绑定到可观察到的斯托夫特拉德
,当任何单个总数发生变化时,将导致重新计算
这是一个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);
}
}
import java.util.ArrayList;
导入java.util.array;
导入java.util.List;
导入java.util.Random;
导入java.util.function.function;
导入java.util.stream.collector;
导入javafx.application.application;
导入javafx.beans.Observable;
导入javafx.beans.binding.Bindings;
导入javafx.beans.binding.DoubleBinding;
导入javafx.beans.property.DoubleProperty;
导入javafx.beans.property.IntegerProperty;
导入javafx.beans.property.ReadOnlyDoubleProperty;
导入javafx.beans.property.ReadOnlyDoubleWrapper;
导入javafx.beans.property.ReadOnlyStringWrapper;
导入javafx.beans.property.SimpleDoubleProperty;
导入javafx.beans.property.SimpleIntegerProperty;
导入javafx.beans.value.observeValue;
导入javafx.collections.FXCollections;
导入javafx.collections.ObservableList;
导入javafx.geometry.HPos;
导入javafx.scene.scene;
导入javafx.scene.control.Button;
导入javafx.scene.control.Label;
导入javafx.scene.control.TableColumn;
导入javafx.scene.control.TableView;
导入javafx.scene.control.TextField;
导入javafx.scene.control.cell.TextFieldTableCell;
导入javafx.scene.layout.BorderPane;
导入javafx.scene.layout.ColumnConstraints;
导入javafx.scene.layout.GridPane;
导入javafx.scene.layout.HBox;
导入javafx.scene.layout.Priority;
导入javafx.stage.stage;
导入javafx.util.converter.DoubleStringConverter;
导入javafx.util.converter.IntegerStringConverter;
公共类TradeTableExample扩展了应用程序{
@凌驾
公共无效开始(阶段primaryStage){
TableView table=新TableView();
table.setEditable(true);
TableColumn nameCol=column(“Name”,trade->new ReadOnlyStringWrapper(trade.getName());
TableColumn volumeCol=列(“卷”,t->t.volumeProperty().asObject());
TableColumn priceCol=column(“Price”,t->t.priceProperty().asObject());
TableColumn totalCol=列(“总计”,贸易::totalProperty);
volumeCol.setCellFactory(TextFieldTableCell.forTableColumn(新的IntegerStringConverter());
priceCol.setCellFactory(TextFieldTableCell.forTableColumn(新的DoubleStringConverter());
table.getColumns().addAll(Arrays.asList(nameCol、volumeCol、priceCol、totalCol));
ObservableList data=FXCollections.observableArrayList(交易->新可观察[]{trade.totalProperty()});
DoubleBinding grandTotal=Bindings.createDoubleBinding(()->
data.stream().collect(collector.summingDouble(Trade::get