JavaFX LineChart - 由于Axis的类型而导致ClassCastException

GGr*_*rec 8 java javafx

如何从FXML文件中指定图表的轴类型?它似乎是默认类型<String, Integer>.如果我将我的可注射字段声明为LineChart<Number, Number> lineChart,并且我创建了一个数据系列(Number, Number,则该程序会抛出一个ClassCastException.

必须使用FXML文件.最糟糕的情况是我手动创建了我的图表.我最好的猜测是这是一个错误.


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;
    }
}
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)

Jam*_*s_D 11

这对我来说很好.我测试了以下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>
Run Code Online (Sandbox Code Playgroud)

和测试应用程序:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你ClassCastException没有看到你的FXML文件(尤其是你正在使用的轴),你有点难以说清楚.但是,从堆栈跟踪中,您似乎使用的是a CategoryAxis,它与a不是类型兼容的LineChart<Number, Number>.

通常,您可以将两种数据类型(一个用于x,一个用于y)声明为所需的任何类型.既然你已经宣布了

@FXML
private LineChart<Number, Number> testChart ;
Run Code Online (Sandbox Code Playgroud)

x变量的类型和y变量的类型都是Number.

参考JavadocsLineChart,a LineChart<X,Y>需要一个Axis<X>x轴和一个Axis<Y>y轴.所以你需要Axis<Number>为每个轴.A CategoryAxisAxis<String>(再次引用Javadocs),因此它不能用作折线图的轴:轴的数据类型与图表的数据类型不兼容.

如果您在Java中尝试了以下内容:

LineChart<Number, Number> testChart ;
testChart = new LineChart<>(new CategoryAxis(), new NumberAxis());
Run Code Online (Sandbox Code Playgroud)

你会得到一个编译错误.由于您(可能)在FXML中初始化轴,并且FXML没有类型检查,因此您可以ClassCastException在运行时获得.

有助于您的代码的一件事是使用正确的类型Series:

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

现在,编译器将检查Series匹配的类型是否与图表的类型Data匹配,以及匹配的类型与类型相匹配Series.(因此series.getData().add(new XYChart.Data("1", 23));会产生编译错误,而不是运行时错误.)由于您在FXML中创建轴,您仍然没有对这些轴进行类型检查,但我认为错误的原因(可能)会变得更清晰.

修复是使用a NumberAxis,如上例所示,而不是你的CategoryAxis.如果你真的想使用a CategoryAxis作为x轴,x值必须是Strings,所以你需要声明LineChart为a LineChart<String, Number>.同样你也可以制作Series一个Series<String, Number>.