原文由 Trisha Gee 在当地时间2019年11月29日发布在 INTELLIJ IDEA BLOG
在这一节,我们看一下如何连接JavaFX折线图到Kotlin Spring Boot服务以实时显示股票价格数据。
在第四步,我们创建了一个JavaFX Spring Boot应用程序显示一个空的折线图。在上一步(第五步),我们整合了WebClientStockClient去连接到股票价格服务。在这一步我们要让折线图实时显示来自Kotlin Spring Boot服务的股票价格数据。
设置好图表数据
- 在stock-ui模块的
ChartController
,创建一个initialize
方法,这个方法会在FXML的字段被值填充完成后被调用。这个initialize
方法会设置好图表数据的来源。 - 在图表上调用
setData
方法并创建一个局部变量data
用于保存系列数据列表,用于传入到这个方法。 - 创建一个
Series
添加到data列表,并传入到seriesData
。 - 创建
seriesData
作为一个字段,即空列表,使用FXCollections.observableArrayList()
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ChartController {
@FXML
private LineChart<String, Double> chart;
private WebClientStockClient webClientStockClient;
private ObservableList<Data<String, Double>> seriesData = FXCollections.observableArrayList();
public ChartController(WebClientStockClient webClientStockClient) {
this.webClientStockClient = webClientStockClient;
}
@FXML
public void initialize() {
ObservableList<XYChart.Series<String, Double>> data = FXCollections.observableArrayList();
data.add(new XYChart.Series<>(seriesData));
chart.setData(data);
}
}
订阅到价格数据
我们需要为图表从某处获取数据。这就是我们为什么添加了webClientStockClient
。
- 在
initialize
方法内调用webClientStockClient
的pricesFor
方法,并传入用于获取股票价格的代号。现在我们可以暂时将这个值设为“SYMBOL”。 - 我们需要将一些东西订阅到由这个调用返回的
Flux
。最简单的做法是调用subscribe
并传入this
。 - 让
ChartController
实现java.util.function.Consumer
并让它消费StockPrice
。 - 实现
Consumer
里边的accept
方法。 - (提示:我们可以通过在类声明上红色的字按下Alt+Enter并选择”implement methods”让IntelliJ IDEA 实现满足接口所需的方法)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class ChartController implements Consumer<StockPrice> {
// fields and constructor here...
@FXML
public void initialize() {
ObservableList<XYChart.Series<String, Double>> data = FXCollections.observableArrayList();
data.add(new XYChart.Series<>(seriesData));
chart.setData(data);
webClientStockClient.pricesFor("SYMBOL").subscribe(this);
}
@Override
public void accept(StockPrice stockPrice) {
}
}
显示价格数据
我们要决定当获取到StockPrice
的时候要做什么。我们从股票价格数据里面添加适当的值到图表的系列数据上。
- 在
accept
内,已新的Data实例调用seriesData.add
。 - 对于Data的y值,我们可以从股票价格的时间获取,然后取其中的“秒”值。这是
int
类型,它需要转换为String
,因此放在一个String.valueOf
调用里。 - (提示:我们可以用后缀补全参数去将一个表达式包含在一个方法调用里以简化对
String.valueOf
的调用,当我们已经输入了表达式)。 - 对于x值,我们使用来自股票价格的
price
值。
1
2
3
4
public void accept(StockPrice stockPrice) {
seriesData.add(new XYChart.Data<>(String.valueOf(stockPrice.getTime().getSecond()),
stockPrice.getPrice()));
}
这看起来不错,但是这个编程模型还有一样东西需要考虑。修改系列数据会反映在对用户界面的更新,会被UI线程绘制。这个accept
方法是运行在不同的线程的,监听着来自后端服务的事件。我们要告知UI线程,当有机会时运行这段代码。
调用Platform.runLater
,并在UI线程传入一个Lambda表达式。
1
2
3
4
5
6
public void accept(StockPrice stockPrice) {
Platform.runLater(() ->
seriesData.add(new XYChart.Data<>(String.valueOf(stockPrice.getTime().getSecond()),
stockPrice.getPrice()))
);
}
运行 chart 应用程序
- 确保后端的Kotlin Spring Boot服务正在运行(
StockSeriviceApplication
)。 - 回到UI模块并运行我们的JavaFX应用程序,
StockUiApplication
。 - 你应该能看到一个JavaFX折线图根据价格数据自动地更新。(见视频里 3:20的例子)
显示股票代码名称
- 将硬编码的股票代号提取到一个局部变量,我们想在不止一个地方用到它。
- (提示:我们可以使用 IntelliJ IDEA 的 Extract Variable(文档) (视频) 轻松完成此操作。)
- 我们可以给系列一个标签,通过在
Series
的构造器传入股票代码作为参数。
1
2
3
4
5
6
7
8
public void initialize() {
String symbol = "SYMBOL";
ObservableList<XYChart.Series<String, Double>> data = FXCollections.observableArrayList();
data.add(new XYChart.Series<>(symbol, seriesData));
chart.setData(data);
webClientStockClient.pricesFor(symbol).subscribe(this);
}
现在当我们重新运行应用程序,我们可以看到股票代码出现在系列的标签上。
代码清理
这些代码太多类型信息有点笨拙,让我们简化一下。我们可以在这里提到的很多类的名称上按下Alt+Enter去让IntelliJ IDEA建议自动执行这些更改。
- 导入Series本身以移除不必要的XYChart前缀。
- 为
observableArrayList
添加一个静态导入,这样我们就不用到处重复FXCollections
。 - 添加一个
valueOf
导入,使得设置图表数据那行更简短一些。 - 导入
Data
类以移除其它重复的XYChart
。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
@Component
public class ChartController implements Consumer<StockPrice> {
@FXML
private LineChart<String, Double> chart;
private WebClientStockClient webClientStockClient;
private ObservableList<Data<String, Double>> seriesData = observableArrayList();
public ChartController(WebClientStockClient webClientStockClient) {
this.webClientStockClient = webClientStockClient;
}
@FXML
public void initialize() {
String symbol = "SYMBOL";
ObservableList<Series<String, Double>> data = observableArrayList();
data.add(new Series<>(symbol, seriesData));
chart.setData(data);
webClientStockClient.pricesFor(symbol).subscribe(this);
}
@Override
public void accept(StockPrice stockPrice) {
Platform.runLater(() ->
seriesData.add(new Data<>(valueOf(stockPrice.getTime().getSecond()),
stockPrice.getPrice()))
);
}
}
这次重构并不会改变代码的行为,所以我们重新运行看到一切都跟之前一样可用。
总结
用这几行代码,我们创建了一个JavaFX应用程序使用了Spring 的WebClient去连接到 Spring Boot 服务,订阅到价格数据流并实时将股票价格数据绘制在折线图上。
全部代码在 GitHub:https://github.com/zwt-io/rsb/