我正试图找到一种方法来更新JavaFX CategoryAxis()的类别。我制作了一个可观察的类别列表,并且他们也在plot()函数中进行了更新。但是,如果我尝试在系列中添加一个新项目,我会得到一个java.lang.IllegalStateException。虽然我知道,不是状态导致错误,而且动态添加似乎是问题所在。下面我附上了我的代码。
package application;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedBarChart;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
final XYChart.Series series1 = new XYChart.Series();
final XYChart.Series series2 = new XYChart.Series();
ObservableList<XYChart.Data> xyList1 = FXCollections.observableArrayList();
ObservableList<XYChart.Data> xyList2 = FXCollections.observableArrayList();
ObservableList<String> myXaxis = FXCollections.observableArrayList();
int i;
@Override public void start(Stage stage) {
stage.setTitle("Line Chart Sample");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
xAxis.setLabel("Month");
final StackedBarChart<String,Number> lineChart =
new StackedBarChart<String,Number>(xAxis,yAxis);
lineChart.setTitle("Woohoo, 2010");
lineChart.setAnimated(false);
series1.setName("Test 1");
series2.setName("test 2");
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
plot();
}
}, 0, 1000);
Scene scene = new Scene(lineChart,800,600);
xAxis.setCategories(myXaxis);
XYChart.Series XYSeries1 = new XYChart.Series(xyList1);
XYChart.Series XYSeries2 = new XYChart.Series(xyList2);
lineChart.getData().addAll(XYSeries1,XYSeries2);
i = 0;
stage.setScene(scene);
stage.show();
}
public void plot() {
SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
Date date = new Date();
date.setTime(date.getTime() + i++ * 11111);
myXaxis.add(dateFormat.format(date));
System.out.println(myXaxis);
// with the line below uncommented the application breaks. Without the x-axis is updated as intended.
//xyList1.add(new XYChart.Data(dateFormat.format(date), Math.random()*10));
}
public static void main(String[] args) {
launch(args);
}
}
答案 0 :(得分:2)
问题 - JavaFX应用程序线程的场景图节点的传递修改
不要在主JavaFX应用程序线程之外修改场景节点(甚至是场景节点所依赖的可观察数据列表)(这是非法的 - 因为你收到的IllegalStateException状态)。
Timer线程不会在JavaFX应用程序线程上运行。
潜在修复
有几种方法可以解决这个问题:
plot()
来电。在这两个选项中,我认为我更喜欢第二种选择,但两者都可以。
计时器样式解决方案
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
Platform.runLater(new Runnable() {
@Override public void run() {
plot();
}
});
}
}, 0, 1000);
时间线样式解决方案
Timeline timeline = new Timeline(
new KeyFrame(
Duration.ZERO,
new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent actionEvent) {
plot();
}
}
),
new KeyFrame(
Duration.seconds(1)
)
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
基于替代服务的解决方案
我建议您阅读concurrency in JavaFX。
另一种方法是将Task或Service与ScheduledExecutorService结合使用,并通过Platform.runLater()
运行更新。然而,这种解决方案虽然复杂而灵活,但比您所描述的问题更加复杂,并且应该首选简单的基于时间轴或定时器的解决方案。如果每个脉冲导致执行耗时的算法或大量基于网络或文件的I / O,则更复杂的基于服务的解决方案是合适的。