I'm trying to add a horizontal line to a linechart in JavaFX. Found this post in SO and now trying to modify it to work in my case, however I'm getting an error, and hopefully someone can see what is the problem here.
Here is the controller code:
package gems.view;
public class GUImainController implements Initializable, Observer
{
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
private ArrayList<Float> tempArr = new ArrayList<>();
private ArrayList<String> tempTimeArray = new ArrayList<>();
private Series<String, Number> tempChart = new Series<>();
@FXML
private LineChartWithMarker<String, Number> lcTemperature = new LineChartWithMarker<>(xAxis,yAxis);
@Override
public void initialize(URL arg0, ResourceBundle arg1)
{
yAxis.setPrefWidth(35);
xAxis.setLabel("Temperature \\u00b0");
yAxis.setLabel("Time");
tempChart = new XYChart.Series<String, Number>();
lcTemperature.getData().add(tempChart);
tempArr.add(observable.getLatestMeasurementsFromServer().getTemperature());
tempTimeArray.add(observable.getLatestMeasurementsFromServer().getDateTime().getTime());
for (int i = 0; i < tempArr.size(); i++)
{
tempChart.getData().add(new XYChart.Data<>(tempTimeArray.get(i), tempArr.get(i)));
}
tempChart.getNode().lookup(".chart-series-line").setStyle("-fx-stroke: orange;");
String s = "test";
Data<String, Number> verticalMarker = new Data<>(s, 20);
lcTemperature.addHorizontalValueMarker(verticalMarker);
}
Here is the LineChartWithMarker class:
package gems.view;
import java.util.Objects;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.shape.Line;
public class LineChartWithMarker<X,Y> extends LineChart
{
private ObservableList<Data<X, Y>> horizontalMarkers;
public LineChartWithMarker(CategoryAxis xAxis, NumberAxis yAxis)
{
super(xAxis, yAxis);
horizontalMarkers = FXCollections.observableArrayList(data -> new Observable[] {data.YValueProperty()});
horizontalMarkers.addListener((InvalidationListener)observable -> layoutPlotChildren());
}
public void addHorizontalValueMarker(Data<X, Y> marker)
{
Objects.requireNonNull(marker, "the marker must not be null");
if (horizontalMarkers.contains(marker)) return;
Line line = new Line();
marker.setNode(line );
getPlotChildren().add(line);
horizontalMarkers.add(marker);
}
public void removeHorizontalValueMarker(Data<X, Y> marker) {
Objects.requireNonNull(marker, "the marker must not be null");
if (marker.getNode() != null) {
getPlotChildren().remove(marker.getNode());
marker.setNode(null);
}
horizontalMarkers.remove(marker);
}
@Override
protected void layoutPlotChildren() {
super.layoutPlotChildren();
for (Data<X, Y> horizontalMarker : horizontalMarkers) {
Line line = (Line) horizontalMarker.getNode();
line.setStartX(0);
line.setEndX(getBoundsInLocal().getWidth());
line.setStartY(getYAxis().getDisplayPosition(horizontalMarker.getYValue()) + 0.5); // 0.5 for crispness
line.setEndY(line.getStartY());
line.toFront();
}
}
}
And here is the FXML (just the part for line chart):
<LineChart fx:id="lcTemperature" minWidth="100.0" prefHeight="155.0" prefWidth="708.0" BorderPane.alignment="CENTER">
<xAxis>
<CategoryAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis autoRanging="false" lowerBound="20.0" minorTickCount="10" minorTickLength="10.0" side="LEFT" tickLabelGap="10.0" tickLength="20.0" tickUnit="1.0" upperBound="30.0" />
</yAxis>
</LineChart>
Herew is the error stack:
Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at sun.launcher.LauncherHelper$FXHelper.main(Unknown Source)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$154(LauncherImpl.java:182)
at java.lang.Thread.run(Unknown Source)
Caused by: javafx.fxml.LoadException:
/C:/My%20Projects/GEMS_SEP2/GEMS_SEP2/bin/FXML/GUIgems.fxml:42
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:2409)
at gems.view.GUIuserClient.start(GUIuserClient.java:33)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$161(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$174(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
... 1 more
Caused by: java.lang.IllegalArgumentException: Can not set gems.view.LineChartWithMarker field gems.view.GUImainController.lcTemperature to javafx.scene.chart.LineChart
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(Unknown Source)
at java.lang.reflect.Field.set(Unknown Source)
at javafx.fxml.FXMLLoader.injectFields(FXMLLoader.java:1163)
at javafx.fxml.FXMLLoader.access$1600(FXMLLoader.java:103)
at javafx.fxml.FXMLLoader$ValueElement.processValue(FXMLLoader.java:857)
at javafx.fxml.FXMLLoader$ValueElement.processEndElement(FXMLLoader.java:765)
at javafx.fxml.FXMLLoader.processEndElement(FXMLLoader.java:2823)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2532)
... 12 more
Exception running application gems.view.GUIuserClient
By the way, GUIgems.fxml:42 is the last line of the FXML code I have posted above.
Also, its maybe worth mentioning that the line chart works fine if I am using a simple LineChart, like this:
@FXML
private LineChart<String, Number> lcTemperature;
It does not work when I write like this:
@FXML
private LineChartWithMarker<String, Number> lcTemperature = new LineChartWithMarker<>(xAxis,yAxis);