私は買い手の売り注文、価格などのリストを更新するバックグラウンドスレッドを持っています。私はブローカーから非同期的にデータを取得します。リストの更新はバックグラウンドスレッドで順番に発生することが重要です。JavaFXテーブルのバックグラウンドスレッドからのリストの変更を表示するにはどうすればよいですか?
「Not on FXアプリケーションスレッドIllegalStateException」のリスクなしに、私のjavafxテーブルにmainPortfolioListを表示したいとします。私が見つけた最も近い解決策はJavaFX refresh TableView threadでした。しかし、私が理解しているように、リストが別のスレッドにある場合、これは機能しません。
私はかなりJavaで新しいですし、addlistenerで私の問題を解決しようとしました。私はこれまでに何をしたいのか、私が何をしたのかを示す簡単な例を作った。
メインポートフォリオリストの更新をJavaFXテーブルに表示するにはどうすればよいですか?
PortfolioController
import application.Test;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.converter.NumberStringConverter;
public class PortfolioController {
@FXML private Button btnTest;
@FXML private TableView<Portfolio> tblPortfolio;
@FXML private TableColumn<Portfolio, String> colSymbol;
@FXML private TableColumn<Portfolio, Number> colQuantity;
@FXML private TableColumn<Portfolio, Number> colPrice;
private ObservableList<Portfolio> fxPortfolioList;
@FXML
private void initialize() {
tblPortfolio.setEditable(true);
colSymbol.setCellValueFactory(data -> data.getValue().colSymbolProperty());
colQuantity.setCellValueFactory(data -> data.getValue().colQuantityProperty());
colPrice.setCellValueFactory(data -> data.getValue().colPriceProperty());
colSymbol.setCellFactory(TextFieldTableCell.forTableColumn());
colQuantity.setCellFactory(TextFieldTableCell.<Portfolio, Number>forTableColumn(
new NumberStringConverter("#,##0.00")));
colQuantity.setOnEditCommit(event -> {
int newValue = event.getNewValue().intValue();
event.getRowValue().setColQuantity(newValue);});
colPrice.setCellFactory(TextFieldTableCell.<Portfolio, Number>forTableColumn(
new NumberStringConverter("#,##0.00")));
fxPortfolioList = FXCollections.observableArrayList();
tblPortfolio.setItems(fxPortfolioList);
Test.mainPortfolioList.addListener((ListChangeListener.Change<? extends Portfolio> c) -> {
while (c.next()) {
if (c.wasAdded()) {
Platform.runLater(() -> {
for (Portfolio asset : c.getAddedSubList()) {
fxPortfolioList.add(asset);
}
});
} else if (c.wasRemoved()) {
Platform.runLater(() -> {
for (Portfolio asset : c.getRemoved()) {
fxPortfolioList.remove(asset);
}
});
} else if (c.wasUpdated()) {
Platform.runLater(() -> {
for (int i = c.getFrom(); i < c.getTo(); ++i) {
fxPortfolioList.set(i, c.getList().get(i));
}
});
}
}
});
}
@FXML
void btnTestClicked(ActionEvent event) {
Test test = new Test();
test.dataStream(this);
}
}
ポートフォリオ
import javafx.beans.Observable;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.util.Callback;
public class Portfolio {
private final StringProperty colSymbol;
private final IntegerProperty colQuantity;
private final DoubleProperty colPrice;
public Portfolio(String symbol, int quantity, double price) {
this.colSymbol = new SimpleStringProperty(symbol);
this.colQuantity = new SimpleIntegerProperty(quantity);
this.colPrice = new SimpleDoubleProperty(price);
}
// extractor
public static Callback<Portfolio, Observable[]> extractor() {
return (Portfolio p) -> new Observable[] {
p.colSymbolProperty(),
p.colQuantityProperty(),
p.colPriceProperty(),
};
}
// property
public StringProperty colSymbolProperty() {
return colSymbol;
}
public IntegerProperty colQuantityProperty() {
return colQuantity;
}
public DoubleProperty colPriceProperty() {
return colPrice;
}
// getter
public String getColSymbol() {
return colSymbol.get();
}
public int getColQuantity() {
return colQuantity.get();
}
public double getColPrice() {
return colPrice.get();
}
// setter
public void setColSymbol(String newValue) {
colSymbol.set(newValue);
}
public void setColQuantity(int newValue) {
colQuantity.set(newValue);
}
public void setColPrice(double newValue) {
colPrice.set(newValue);
}
}
import controller.portfolio.Portfolio;
import controller.portfolio.PortfolioController;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
public class Test {
public static ObservableList<Portfolio> mainPortfolioList =
FXCollections.observableArrayList(Portfolio.extractor());
public void dataStream(PortfolioController portfolioController) {
// Need to be sequentially
// The task only simulates simplified operations
Runnable task =() -> {
// add stock
mainPortfolioList.add(new Portfolio("AAPL", 13, 153.03));
mainPortfolioList.add(new Portfolio("MSFT", 31, 67.51));
// Change the quantity
for (Portfolio asset : mainPortfolioList) {
if (asset.getColSymbol().equals("AAPL")) {
asset.setColQuantity(55);
}
}
// run price updates
for (int k = 0; k < 15; k++) {
for (int m = 0; m < mainPortfolioList.size(); m++) {
double random = Math.random() * 50 + 1;
String symbol = mainPortfolioList.get(m).getColSymbol();
setTickPrice(symbol, 4, random);
randomSleep();
}
}
// remove stock
for (Portfolio asset : mainPortfolioList) {
if (asset.getColSymbol().equals("AAPL")) {
mainPortfolioList.remove(asset);
}
}
};
Thread t = new Thread(task, "Simulation");
t.setDaemon(true);
t.start();
}
public void setTickPrice(String symbol, int tickType, double price) {
for (Portfolio asset : mainPortfolioList) {
if (asset.getColSymbol().equals(symbol)) {
switch(tickType){
case 4: // Last Price
asset.setColPrice(price);
break;
}
}
}
}
private void randomSleep() {
try {
Thread.sleep((int)(Math.random() * 300));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
メイン試験シミュレーション
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("/view/Portfolio.fxml"));
Scene scene = new Scene(root, 500, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
画面上の何かを変更します(IBを想定)、リーダ・スレッドから来る3210
図
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<BorderPane prefHeight="600.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="controller.portfolio.PortfolioController">
<top>
</top>
<center>
<TableView fx:id="tblPortfolio" prefHeight="200.0" prefWidth="200.0" tableMenuButtonVisible="true" BorderPane.alignment="CENTER">
<columns>
<TableColumn fx:id="colSymbol" maxWidth="80.0" minWidth="60.0" prefWidth="-1.0" text="Symbol" />
<TableColumn fx:id="colQuantity" maxWidth="60.0" minWidth="40.0" prefWidth="-1.0" text="Quantity" />
<TableColumn fx:id="colPrice" maxWidth="69.0" minWidth="49.0" prefWidth="-1.0" text="Price" />
</columns>
<columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
</columnResizePolicy>
</TableView>
</center>
<bottom>
</bottom>
<top>
<HBox BorderPane.alignment="CENTER">
<children>
<Button fx:id="btnTest" mnemonicParsing="false" onAction="#btnTestClicked" text="Test">
<HBox.margin>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</top>
</BorderPane>
あなたはインタラクティブブローカーにぴったりです。私はあなたの最初の解決策を避けることを望みました。 2番目はかなり面白いです。私は後でそれを試みます:) – akut
私はあなたがそれぞれのラッパーメソッドで何をしているのか分かりませんが、考慮すべき2つのことがあります。 'Platform.runLater'はGUIスレッドのすべての処理を行います。長ければ、GUIスレッドは応答しなくなります。したがって、最小限の金額だけを実行してください。しかし、リーダースレッドの処理が長くなると、最終的にソケットをブロックする危険性があります。これは現代のハードウェアでは通常問題ではありません。極端な状況で別の計算スレッドを必要とするかどうかを判断したり、それらのニーズをバランスさせたり、各スレッドでどれくらいのことをするかはあなた次第です。 – brian