Wie lässt sich ein Liniendiagramm erstellen?

In einem Liniendiagramm werden Daten in einem zweidimensionalen Koordinatensystem erfasst. Sie werden durch mit Linien verbundene Punkte dargestellt. In JavaFX kann ein Liniendiagramm mit Hilfe der Klasse javafx.scene.chart.LineChart erstellt werden.

Das Beispiel demonstriert das Zeichnen eines Liniendiagramms, in dem die Anzahl der errungenen Zweitstimmen einiger Parteien bei den Bundestagswahlen von 1980 bis 2021 dargestellt werden[1]. Die X-Achse repräsentiert die Zeitachse, die Y-Achse gibt den Stimmenanteil in Prozent an.

Beide Achsen werden durch Objekte der Klasse NumberAxis realisiert und mit einem Labeltext zur Bezeichnung der jeweils erfassten Größe versehen. Sie werden dem Konstruktor bei der Erzeugung der LineChart übergeben. Da auf beiden Achsen numerische Werte aufgetragen werden, müssen dem generischen Typ LineChart beide Datentypen als Number bekanntgemacht werden.

Für jede Partei wird eine Datengruppe vom Typ XYChart.Series erstellt. Sie speichert die Werte in Form mehrerer XYChart.Data-Objekte. In jedem werden jeweils ein Wahljahr und der bei dieser Wahl erreichte Stimmenanteil gespeichert. Die Werte werden dem Konstruktor übergeben.
Im Einzelnen geschieht dies so, dass durch getData() die ObservableList des Series-Objektes ermittelt und dieser dann durch add() das Data-Objekt übergeben wird. Auf diese Weise werden die Werte für jede Partei in einer separaten Liste abgelegt.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
 
public class LineChartBsp extends Application {
 
    @SuppressWarnings({ "unchecked" })
    @Override public void start(Stage stage) {
        stage.setTitle("Line Chart Beispiel");
        final NumberAxis xAxis = new NumberAxis(1980, 2022, 1);
        final NumberAxis yAxis = new NumberAxis();
        xAxis.setLabel("Jahr");
        yAxis.setLabel("Stimmenanteil [%]");
        LineChart<Number,Number> lineChart = new LineChart<Number,Number>(xAxis, yAxis);
                
        lineChart.setTitle("Bundestagswahlen");

        XYChart.Series<Number, Number> seriesCdu = new XYChart.Series<Number, Number>();
        seriesCdu.setName("CDU/CSU");
        seriesCdu.getData().add(new XYChart.Data<Number, Number>(1980, 44.5));
        seriesCdu.getData().add(new XYChart.Data<Number, Number>(1983, 48.8));
        seriesCdu.getData().add(new XYChart.Data<Number, Number>(1987, 44.3));
        seriesCdu.getData().add(new XYChart.Data<Number, Number>(1990, 43.8));
        seriesCdu.getData().add(new XYChart.Data<Number, Number>(1994, 41.4));
        seriesCdu.getData().add(new XYChart.Data<Number, Number>(1998, 35.1));
        seriesCdu.getData().add(new XYChart.Data<Number, Number>(2002, 38.5));
        seriesCdu.getData().add(new XYChart.Data<Number, Number>(2005, 35.2));
        seriesCdu.getData().add(new XYChart.Data<Number, Number>(2009, 33.8));
        seriesCdu.getData().add(new XYChart.Data<Number, Number>(2013, 41.5));
        seriesCdu.getData().add(new XYChart.Data<Number, Number>(2017, 32.9));
        seriesCdu.getData().add(new XYChart.Data<Number, Number>(2021, 24.1));
        
        XYChart.Series<Number, Number> seriesSpd = new XYChart.Series<Number, Number>();
        seriesSpd.setName("SPD");
        seriesSpd.getData().add(new XYChart.Data<Number, Number>(1980, 42.9));
        seriesSpd.getData().add(new XYChart.Data<Number, Number>(1983, 38.8));
        seriesSpd.getData().add(new XYChart.Data<Number, Number>(1987, 37.0));
        seriesSpd.getData().add(new XYChart.Data<Number, Number>(1990, 33.5));
        seriesSpd.getData().add(new XYChart.Data<Number, Number>(1994, 36.4));
        seriesSpd.getData().add(new XYChart.Data<Number, Number>(1998, 40.9));
        seriesSpd.getData().add(new XYChart.Data<Number, Number>(2002, 38.5));
        seriesSpd.getData().add(new XYChart.Data<Number, Number>(2005, 34.2));
        seriesSpd.getData().add(new XYChart.Data<Number, Number>(2009, 23.0));
        seriesSpd.getData().add(new XYChart.Data<Number, Number>(2013, 25.7));
        seriesSpd.getData().add(new XYChart.Data<Number, Number>(2017, 20.5));
        seriesSpd.getData().add(new XYChart.Data<Number, Number>(2021, 25.7));
        
        XYChart.Series<Number, Number> seriesGruen = new XYChart.Series<Number, Number>();
        seriesGruen.setName("Gr\u00fcne");
        seriesGruen.getData().add(new XYChart.Data<Number, Number>(1980, 1.5));
        seriesGruen.getData().add(new XYChart.Data<Number, Number>(1983, 5.6));
        seriesGruen.getData().add(new XYChart.Data<Number, Number>(1987, 8.3));
        seriesGruen.getData().add(new XYChart.Data<Number, Number>(1990, 5.1));
        seriesGruen.getData().add(new XYChart.Data<Number, Number>(1994, 7.3));
        seriesGruen.getData().add(new XYChart.Data<Number, Number>(1998, 8.7));
        seriesGruen.getData().add(new XYChart.Data<Number, Number>(2002, 8.6));
        seriesGruen.getData().add(new XYChart.Data<Number, Number>(2005, 8.1));
        seriesGruen.getData().add(new XYChart.Data<Number, Number>(2009, 10.7));
        seriesGruen.getData().add(new XYChart.Data<Number, Number>(2013, 8.4));
        seriesGruen.getData().add(new XYChart.Data<Number, Number>(2017, 8.9));
        seriesGruen.getData().add(new XYChart.Data<Number, Number>(2021, 14.8));

        XYChart.Series<Number, Number> seriesFdp = new XYChart.Series<Number, Number>();
        seriesFdp.setName("FDP");
        seriesFdp.getData().add(new XYChart.Data<Number, Number>(1980, 10.6));
        seriesFdp.getData().add(new XYChart.Data<Number, Number>(1983, 7.0));
        seriesFdp.getData().add(new XYChart.Data<Number, Number>(1987, 9.1));
        seriesFdp.getData().add(new XYChart.Data<Number, Number>(1990, 11.0));
        seriesFdp.getData().add(new XYChart.Data<Number, Number>(1994, 6.9));
        seriesFdp.getData().add(new XYChart.Data<Number, Number>(1998, 6.2));
        seriesFdp.getData().add(new XYChart.Data<Number, Number>(2002, 7.4));
        seriesFdp.getData().add(new XYChart.Data<Number, Number>(2005, 9.8));
        seriesFdp.getData().add(new XYChart.Data<Number, Number>(2009, 14.6));
        seriesFdp.getData().add(new XYChart.Data<Number, Number>(2013, 4.8));
        seriesFdp.getData().add(new XYChart.Data<Number, Number>(2017, 10.7));
        seriesFdp.getData().add(new XYChart.Data<Number, Number>(2021, 11.5));
        
        XYChart.Series<Number, Number> seriesAfd = new XYChart.Series<Number, Number>();
        seriesAfd.setName("AFD");
        seriesAfd.getData().add(new XYChart.Data<Number, Number>(1980, 0));
        seriesAfd.getData().add(new XYChart.Data<Number, Number>(1983, 0));
        seriesAfd.getData().add(new XYChart.Data<Number, Number>(1987, 0));
        seriesAfd.getData().add(new XYChart.Data<Number, Number>(1990, 0));
        seriesAfd.getData().add(new XYChart.Data<Number, Number>(1994, 0));
        seriesAfd.getData().add(new XYChart.Data<Number, Number>(1998, 0));
        seriesAfd.getData().add(new XYChart.Data<Number, Number>(2002, 0));
        seriesAfd.getData().add(new XYChart.Data<Number, Number>(2005, 0));
        seriesAfd.getData().add(new XYChart.Data<Number, Number>(2009, 0));
        seriesAfd.getData().add(new XYChart.Data<Number, Number>(2013, 4.7));
        seriesAfd.getData().add(new XYChart.Data<Number, Number>(2017, 12.6));
        seriesAfd.getData().add(new XYChart.Data<Number, Number>(2021, 10.3));
        
        XYChart.Series<Number, Number> seriesLinke = new XYChart.Series<Number, Number>();
        seriesLinke.setName("Die Linke");
        seriesLinke.getData().add(new XYChart.Data<Number, Number>(1980, 0));
        seriesLinke.getData().add(new XYChart.Data<Number, Number>(1983, 0));
        seriesLinke.getData().add(new XYChart.Data<Number, Number>(1987, 0));
        seriesLinke.getData().add(new XYChart.Data<Number, Number>(1990, 2.4));
        seriesLinke.getData().add(new XYChart.Data<Number, Number>(1994, 4.4));
        seriesLinke.getData().add(new XYChart.Data<Number, Number>(1998, 5.1));
        seriesLinke.getData().add(new XYChart.Data<Number, Number>(2002, 4.0));
        seriesLinke.getData().add(new XYChart.Data<Number, Number>(2005, 8.7));
        seriesLinke.getData().add(new XYChart.Data<Number, Number>(2009, 11.9));
        seriesLinke.getData().add(new XYChart.Data<Number, Number>(2013, 8.6));
        seriesLinke.getData().add(new XYChart.Data<Number, Number>(2017, 9.2));
        seriesLinke.getData().add(new XYChart.Data<Number, Number>(2021, 4.9));
        
        Scene scene  = new Scene(lineChart,800,600);
        String stylesheet = getClass().getResource("/styles/styles.css").toExternalForm();
        scene.getStylesheets().add(stylesheet);
        lineChart.getData().addAll(seriesSpd, seriesFdp, seriesGruen, seriesCdu, seriesAfd, seriesLinke);
       
        stage.setScene(scene);
        stage.show();
    }
 
    public static void main(String[] args) {
        launch(args);
    }
}

Das LineChart-Objekt stellt ebenfalls eine ObservableList bereit. Ihr werden in Zeile 114 die einzelnen Gruppen übergeben.

Im Diagramm werden die einzelnen Datenpunkte durch kleine Kreise dargestellt. Möchte man diese vermeiden, so kann dies durch Aufruf von lineChart.setCreateSymbols(false); erfolgen.

Liniendiagramm der Bundestagswahlergebnisse zwischen 1980 und 2021

Farbkonfiguration

Die Farbe, in der die einzelnen Gruppen im Diagramm dargestellt werden, wird durch das JavaFX-Default-Stylesheet1 gesteuert. Die Zuweisung der Farben kann hierbei durch die Reihenfolge bei der Parameterübergabe an die Methode addAll() (Zeile 114) gesteuert werden. Sollen wie im vorliegenden Fall auch eigene Farben Verwendung finden, können diese durch Überschreiben der Stylesheets gesetzt werden. Hierzu werden diese in den Zeilen 112 und 113 aus einer Datei geladen und an die Scene übergeben.

/* Kreise und Legende */
.default-color3.chart-line-symbol { -fx-background-color: #000000, white; }
.default-color4.chart-line-symbol { -fx-background-color: #aa5030, white; }
/* Linie */
.default-color3.chart-series-line { -fx-stroke: #000000; }
.default-color4.chart-series-line { -fx-stroke: #aa5030; }
/* Hintergrund */
.chart-plot-background, .chart, .chart-legend { -fx-background-color: #bbb; }

Im Beispiel findet die Einfärbung der als 4. und 5. Gruppe (AFD, CDU) geladenen Elemente statt. Die verwendeten Selektoren .default-colorX.chart-line-symbol und .default-colorX.chart-series-line beziehen sich auf die kleinen Kreise in Diagramm und Legende, sowie auf die Linie selbst. Das X muss jeweils durch die Position in der Ladereihenfolge beginnend bei 0 ersetzt werden. Auf diese Weise ist eine Vergabe von acht verschiedenen Farben möglich.

Quellen
  1. Daten zu den Bundestagswahlen: https://de.wikipedia.org/wiki/Bundestagswahl
  2. https://docs.oracle.com/javase/8/javafx/user-interface-tutorial/line-chart.htm#CIHGBCFI

1) Das Default-Stylesheet findet sich unter dem Pfad jfxrt.jar/com/sun/javafx/scene/control/skin/caspian/caspian.css