When running the test app below you get a NPE thrown at the start. I expect this is because it starts changing the data before the chart is 100% built, but we need to defend against the NPE error.
java.lang.NullPointerException
at javafx.scene.chart.LineChart.dataItemRemoved(LineChart.java:168)
at javafx.scene.chart.XYChart.dataItemsChanged(XYChart.java:306)
at javafx.scene.chart.XYChart.access$2500(XYChart.java:35)
at javafx.scene.chart.XYChart$Series$1.onChanged(XYChart.java:1122)
at com.sun.javafx.collections.ObservableListWrapper.callObservers(ObservableListWrapper.java:62)
at com.sun.javafx.collections.ObservableListWrapper.set(ObservableListWrapper.java:196)
at base$1.handle(base.java:35)
at base$1.handle(base.java:1)
at com.sun.scenario.animation.shared.TimelineClipCore.visitKeyFrame(TimelineClipCore.java:185)
at com.sun.scenario.animation.shared.TimelineClipCore.playTo(TimelineClipCore.java:132)
at javafx.animation.Timeline.impl_playTo(Timeline.java:144)
at com.sun.scenario.animation.shared.GeneralClipEnvelope.visitCycle(GeneralClipEnvelope.java:255)
at com.sun.scenario.animation.shared.GeneralClipEnvelope.timePulse(GeneralClipEnvelope.java:184)
at javafx.animation.Animation.impl_timePulse(Animation.java:795)
at com.sun.scenario.animation.shared.AnimationPulseReceiver.timePulse(AnimationPulseReceiver.java:83)
at com.sun.scenario.animation.AbstractMasterTimer.timePulseImpl(AbstractMasterTimer.java:332)
at com.sun.scenario.animation.AbstractMasterTimer$MainLoop.run(AbstractMasterTimer.java:245)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:340)
at com.sun.javafx.tk.quantum.QuantumToolkit$9.run(QuantumToolkit.java:284)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
at com.sun.glass.ui.win.WinApplication$1$1.run(WinApplication.java:49)
at java.lang.Thread.run(Unknown Source)
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
import javafx.util.Duration;
public class LineChartLeakTest extends Application {
@Override public void start(Stage stage) throws Exception {
LineChart<Number,Number> lineChart = new LineChart<Number, Number>(new NumberAxis(), new NumberAxis());
// create single series with 50 items to start
final LineChart.Series<Number,Number> series = new XYChart.Series<Number,Number>();
for (int i=0; i< 50; i++) series.getData().add(new XYChart.Data<Number, Number>(Math.random(), Math.random()));
lineChart.getData().add(series);
stage.setScene(new Scene(lineChart,800,600));
stage.setVisible(true);
Timeline timeline = new Timeline();
timeline.setCycleCount(Animation.INDEFINITE);
timeline.getKeyFrames().add(new KeyFrame(new Duration(100), new EventHandler<ActionEvent>() {
public void handle(ActionEvent actionEvent) {
if (Math.random() < 0.9) { // 90% of the time replace a data item
if (!series.getData().isEmpty()) {
series.getData().set(
(int)(Math.random()*(series.getData().size()-1)),
new XYChart.Data<Number, Number>(Math.random(), Math.random())
);
}
} else { // otherwise add a new data item
series.getData().add(new XYChart.Data<Number, Number>(Math.random(), Math.random()));
}
}
}));
timeline.play();
}
public static void main(String[] args) {
launch(args);
}
}
java.lang.NullPointerException
at javafx.scene.chart.LineChart.dataItemRemoved(LineChart.java:168)
at javafx.scene.chart.XYChart.dataItemsChanged(XYChart.java:306)
at javafx.scene.chart.XYChart.access$2500(XYChart.java:35)
at javafx.scene.chart.XYChart$Series$1.onChanged(XYChart.java:1122)
at com.sun.javafx.collections.ObservableListWrapper.callObservers(ObservableListWrapper.java:62)
at com.sun.javafx.collections.ObservableListWrapper.set(ObservableListWrapper.java:196)
at base$1.handle(base.java:35)
at base$1.handle(base.java:1)
at com.sun.scenario.animation.shared.TimelineClipCore.visitKeyFrame(TimelineClipCore.java:185)
at com.sun.scenario.animation.shared.TimelineClipCore.playTo(TimelineClipCore.java:132)
at javafx.animation.Timeline.impl_playTo(Timeline.java:144)
at com.sun.scenario.animation.shared.GeneralClipEnvelope.visitCycle(GeneralClipEnvelope.java:255)
at com.sun.scenario.animation.shared.GeneralClipEnvelope.timePulse(GeneralClipEnvelope.java:184)
at javafx.animation.Animation.impl_timePulse(Animation.java:795)
at com.sun.scenario.animation.shared.AnimationPulseReceiver.timePulse(AnimationPulseReceiver.java:83)
at com.sun.scenario.animation.AbstractMasterTimer.timePulseImpl(AbstractMasterTimer.java:332)
at com.sun.scenario.animation.AbstractMasterTimer$MainLoop.run(AbstractMasterTimer.java:245)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:340)
at com.sun.javafx.tk.quantum.QuantumToolkit$9.run(QuantumToolkit.java:284)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
at com.sun.glass.ui.win.WinApplication$1$1.run(WinApplication.java:49)
at java.lang.Thread.run(Unknown Source)
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
import javafx.util.Duration;
public class LineChartLeakTest extends Application {
@Override public void start(Stage stage) throws Exception {
LineChart<Number,Number> lineChart = new LineChart<Number, Number>(new NumberAxis(), new NumberAxis());
// create single series with 50 items to start
final LineChart.Series<Number,Number> series = new XYChart.Series<Number,Number>();
for (int i=0; i< 50; i++) series.getData().add(new XYChart.Data<Number, Number>(Math.random(), Math.random()));
lineChart.getData().add(series);
stage.setScene(new Scene(lineChart,800,600));
stage.setVisible(true);
Timeline timeline = new Timeline();
timeline.setCycleCount(Animation.INDEFINITE);
timeline.getKeyFrames().add(new KeyFrame(new Duration(100), new EventHandler<ActionEvent>() {
public void handle(ActionEvent actionEvent) {
if (Math.random() < 0.9) { // 90% of the time replace a data item
if (!series.getData().isEmpty()) {
series.getData().set(
(int)(Math.random()*(series.getData().size()-1)),
new XYChart.Data<Number, Number>(Math.random(), Math.random())
);
}
} else { // otherwise add a new data item
series.getData().add(new XYChart.Data<Number, Number>(Math.random(), Math.random()));
}
}
}));
timeline.play();
}
public static void main(String[] args) {
launch(args);
}
}