JavaFX LineChart - ClassCastException because of the Axis' Type

匿名 (未验证) 提交于 2019-12-03 08:42:37

问题:

How do I specify the axis type for the chart from the FXML file? It seems like the default types are <String, Integer>. If I declare my injectable field as LineChart<Number, Number> lineChart, and the I create a data series with (Number, Number), the program throws a ClassCastException.

It is mandatory for a FXML File to be used. The worst case scenario would be that I created my chart manually. My best guess is that this is a bug.


import java.io.IOException; import java.net.URL; import java.util.ResourceBundle;  import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.chart.LineChart; import javafx.scene.chart.XYChart; import javafx.scene.layout.AnchorPane;  /**  *   * @author ggrec  *  */ public class TestChart implements Initializable {      // ====================== 2. Instance Fields =============================      @FXML     private LineChart<Number, Number> testChart;      private AnchorPane anchorPane;       // ==================== 4. Constructors ====================      public TestChart()     {         final FXMLLoader fxmlLoader = new FXMLLoader( TestChart.class.getResource("testChart.fxml") );         fxmlLoader.setController(this);          try         {             anchorPane = (AnchorPane) fxmlLoader.load();         }         catch (final IOException e)         {             e.printStackTrace();         }     }       // ==================== 5. Creators ====================      @Override     public void initialize(final URL arg0, final ResourceBundle arg1)     {         //      testChart.getXAxis().setAutoRanging(true);         //      testChart.getYAxis().setAutoRanging(true);          testChart.getData().add(getDummyData());     }       // ==================== 7. Getters & Setters ====================      public AnchorPane getAnchorPane()     {         return anchorPane;     }       // ==================== 13. Utility Methods ====================      private XYChart.Series getDummyData()     {         final XYChart.Series series = new XYChart.Series();         series.setName("My portfolio");          series.getData().add(new XYChart.Data<Number, Number>(1, 23)); // Works for ("1", 23)         //      series.getData().add(new XYChart.Data("2", 14));         //      series.getData().add(new XYChart.Data("3", 15));         //      series.getData().add(new XYChart.Data("4", 24));         //      series.getData().add(new XYChart.Data("5", 34));         //      series.getData().add(new XYChart.Data("6", 36));         //      series.getData().add(new XYChart.Data("7", 22));         //      series.getData().add(new XYChart.Data("8", 45));         //      series.getData().add(new XYChart.Data("9", 43));         //      series.getData().add(new XYChart.Data("10", 17));         //      series.getData().add(new XYChart.Data("11", 29));         //      series.getData().add(new XYChart.Data("12", 25));          return series;     } }

java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at javafx.scene.chart.CategoryAxis.invalidateRange(CategoryAxis.java:399) at javafx.scene.chart.XYChart.updateAxisRange(XYChart.java:603) at javafx.scene.chart.XYChart.layoutChartChildren(XYChart.java:620) at javafx.scene.chart.Chart$1.layoutChildren(Chart.java:84) at javafx.scene.Parent.layout(Parent.java:1018)

回答1:

This works fine for me. I tested with the following FXML:

<?xml version="1.0" encoding="UTF-8"?>  <?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.chart.LineChart?> <?import javafx.scene.chart.NumberAxis?>  <AnchorPane xmlns:fx="http://javafx.com/fxml">     <LineChart fx:id="testChart">     <xAxis><NumberAxis /></xAxis>     <yAxis><NumberAxis /></yAxis>     </LineChart> </AnchorPane>

and test application:

import javafx.application.Application; import javafx.scene.Scene; import javafx.stage.Stage;  public class Main extends Application {     @Override     public void start(Stage primaryStage) {         try {             TestChart chart = new TestChart();             Scene scene = new Scene(chart.getAnchorPane(), 600, 400);             primaryStage.setScene(scene);             primaryStage.show();         } catch(Exception e) {             e.printStackTrace();         }     }      public static void main(String[] args) {         launch(args);     } }

It's a little difficult to tell why you are getting a ClassCastException without seeing your FXML file (and in particular the axes you are using). However, from the stack trace, it appears you are using a CategoryAxis, which is not type-compatible with a LineChart<Number, Number>.

In general, you can declare the two data types (one for x and one for y) to be any types you want. Since you've declared

@FXML private LineChart<Number, Number> testChart ;

both the type of the x variable and the type of the y variable are Number.

Referring to the Javadocs for LineChart, a LineChart<X,Y> requires an Axis<X> for the x-axis and an Axis<Y> for the y-axis. So you need an Axis<Number> for each axis. A CategoryAxis is an Axis<String> (again, refer to the Javadocs), so it cannot be used as an axis for your line chart: the data type for the axis is incompatible with the data type for the chart.

If you tried the following in Java:

LineChart<Number, Number> testChart ; testChart = new LineChart<>(new CategoryAxis(), new NumberAxis());

you would get a compile error. Since you're (presumably) initializing the axes in FXML, and FXML has no type checking, you get the ClassCastException at runtime instead.

One thing that would help your code is to use a properly typed Series:

private XYChart.Series<Number, Number> getDummyData() {     final XYChart.Series<Number, Number> series = new XYChart.Series<>();     series.setName("My portfolio");     // ... }

Now the compiler will check that the type of the Series matches the type of the chart, and the type of the Data matches the type of the Series. (So series.getData().add(new XYChart.Data("1", 23)); would give a compile error, rather than a runtime error.) Since you are creating the axes in FXML, you still have no type checking on those but I think the cause of the error would (perhaps) become clearer.

The fix is to use a NumberAxis, as in my example above, instead of your CategoryAxis. If you really want to use a CategoryAxis for the x-axis, the x values have to be Strings, and so you need to declare the LineChart as a LineChart<String, Number>. Similarly you would make the Series a Series<String, Number>.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!