Wie lässt sich ein Säulendiagramm realisieren?

In einem Säulendiagramm werden Daten in einem zweidimensionalen Koordinatensystem erfasst. Die X-Achse repräsentiert eine beliebige Kategorie, die Y-Achse numerische Werte.

Säulendiagramm mit einer Datengruppe

In JavaFX lassen sich Säulendiagramme mit einer oder mehreren Datengruppen durch die Klasse javafx.scene.chart.BarChart realisieren. Wesentliche Gestaltungselemente wie die Säulenbreite und -farbe sind dabei voreingestellt können zumindest teilweise auch konfiguriert werden.
Hierzu werden zuerst ein CategoryAxis-Objekt für String-Literale als X-Achse und ein NumberAxis-Objekt für numerische Werte als Y-Achse gebildet. Beide Achsen-Objekte werden dem Konstruktor der BarChart übergeben. Durch die Methode setLabel() können beiden Achsen Kategoriebezeichner zugeteilt werden.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;

public class BarChartEinfachBsp extends Application {

    @Override
    public void start(Stage stage) {
        stage.setTitle("Mopeds");
        final CategoryAxis x = new CategoryAxis();
        final NumberAxis y = new NumberAxis();
        final BarChart<String, Number> bc = new BarChart<>(x, y);
        bc.setTitle("BMW");
        x.setLabel("Typ");
        y.setLabel("Leistung");

        XYChart.Series<String, Number> serie = new XYChart.Series<String, Number>();
        serie.setName("500ccm-Modelle");
        serie.getData().add(new XYChart.Data<String, Number>("R 32", 8.5));
        serie.getData().add(new XYChart.Data<String, Number>("R 37", 16));
        serie.getData().add(new XYChart.Data<String, Number>("R 47", 18));
        serie.getData().add(new XYChart.Data<String, Number>("R 57", 18));
        serie.getData().add(new XYChart.Data<String, Number>("R 5", 24));
        serie.getData().add(new XYChart.Data<String, Number>("R 51", 24));
        serie.getData().add(new XYChart.Data<String, Number>("R 50", 26));
                                                
        bc.getData().add(serie);
        Scene scene = new Scene(bc, 800, 600);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Die Erfassung der Daten erfolgt als Gruppe in Form eines Objektes der inneren Klasse XYChart.Series<String, Number>. Sie speichert die Daten in einer ObservableList, der durch add() ein neues Datenobjekt hinzugefügt werden kann. Dies wird als Objekt der zweiten innere Klasse von XYChart, XYChart.Data<String, Number>, bereitgestellt. Nach dem gleichen Prinzip wird die gesamte Gruppe (series) in Zeile 32 der Chart hinzugefügt und diese schließlich auf die Scene gesetzt.

Beispiel eines Säulendiagramms mit einer Datengruppe

Säulendiagramm mit mehreren Datengruppen

Die vergleichende Darstellung mehrer Gruppen in einem Säulendiagramm gestaltet sich recht einfach. Hierzu müssen lediglich mehrere Gruppen erzeugt und hinzugefügt werden.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;

public class BarChartDreifachBsp extends Application {

    @SuppressWarnings("unchecked")
    @Override
    public void start(Stage stage) {
        stage.setTitle("Bar Chart Beispiel");
        final CategoryAxis x = new CategoryAxis();
        final NumberAxis y = new NumberAxis();
        final BarChart<String, Number> bc = new BarChart<>(x, y);
        bc.setTitle("BMW Klassiker");
        y.setLabel("Leistung [PS]");

        XYChart.Series<String, Number> serie1 = new XYChart.Series<String, Number>();
        serie1.setName("500ccm-Modelle");
        serie1.getData().add(new XYChart.Data<String, Number>("R 32", 8.5));
        serie1.getData().add(new XYChart.Data<String, Number>("R 37", 16));
        serie1.getData().add(new XYChart.Data<String, Number>("R 47", 18));
        serie1.getData().add(new XYChart.Data<String, Number>("R 57", 18));
        serie1.getData().add(new XYChart.Data<String, Number>("R 5", 24));
        serie1.getData().add(new XYChart.Data<String, Number>("R 51", 24));
        serie1.getData().add(new XYChart.Data<String, Number>("R 50", 26));
        
        XYChart.Series<String, Number> serie2 = new XYChart.Series<String, Number>();
        serie2.setName("250ccm-Modelle");
        serie2.getData().add(new XYChart.Data<String, Number>("R 39", 6.5));
        serie2.getData().add(new XYChart.Data<String, Number>("R 23", 10));
        serie2.getData().add(new XYChart.Data<String, Number>("R 24", 12));
        serie2.getData().add(new XYChart.Data<String, Number>("R 25", 12));
        serie2.getData().add(new XYChart.Data<String, Number>("R 25/2", 12));
        serie2.getData().add(new XYChart.Data<String, Number>("R 25/3", 13));
        serie2.getData().add(new XYChart.Data<String, Number>("R 26", 15));
        serie2.getData().add(new XYChart.Data<String, Number>("R 27", 18));
        
        XYChart.Series<String, Number> serie3 = new XYChart.Series<String, Number>();
        serie3.setName("750ccm-Modelle");
        serie3.getData().add(new XYChart.Data<String, Number>("R 62", 18));
        serie3.getData().add(new XYChart.Data<String, Number>("R 63", 24));
        serie3.getData().add(new XYChart.Data<String, Number>("R 11", 18));
        serie3.getData().add(new XYChart.Data<String, Number>("R 16", 25));
        serie3.getData().add(new XYChart.Data<String, Number>("R 12", 18));
        serie3.getData().add(new XYChart.Data<String, Number>("R 17", 33));
        serie3.getData().add(new XYChart.Data<String, Number>("R 71", 22));
        serie3.getData().add(new XYChart.Data<String, Number>("R 75", 26));
                                                
        Scene scene = new Scene(bc, 800, 600);
        bc.getData().addAll(serie2, serie1, serie3);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
Beispiel eines Säulendiagramms mit mehreren Datengruppen

Farbeinstellungen

Sollen Werte gleicher Elemente kategorieübergreifend verglichen werden, so kann dies auch farblich unterstützt werden. Hierzu müssen die einzelnen Säulen, sowie deren Farben in der Legende angesprochen werden. Dies geschieht über Stylesheets durch die Selektoren .default-colorX.chart-bar und .default-colorX.chart-bar-symbol und der Eigenschaft -fx-bar-fill Das X des Selektors muss hierbei durch eine Zahl zwischen 0 und 7 ersetzt werden. Sie bezieht sich auf die Position eines Eintrags in der Gruppe beginnend bei 0. Somit können acht unterschiedliche Farben vergeben werden. Die Position bestimmt sich übrigens durch die Ladereihenfolge innerhalb der Methode addAll(), hier in Zeile 109.
Das folgende Beispiel demonstriert das Gesagte anhand einer Übersicht über einen fiktiven Getränkekonsum.

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;

public class BarChartMehrfachBsp extends Application {
    private final static String j50 ="1950";
    private final static String j60 ="1960";
    private final static String j70 ="1970";
    private final static String j80 ="1980";
    private final static String j90 ="1990";
    private final static String j00 ="2000";
    private final static String j10 ="2010";

    @SuppressWarnings("unchecked")
    @Override
    public void start(Stage stage) {
        stage.setTitle("Bar Chart Beispiel");
        final CategoryAxis x = new CategoryAxis();
        final NumberAxis y = new NumberAxis();
        final BarChart<String , Number> bc = new BarChart<>(x, y);
        bc.setTitle("Getr\u00e4nkekonsum");
        x.setLabel("Jahr");
        y.setLabel("Menge");

        XYChart.Series<String , Number> serie1 = new XYChart.Series<String
                    , Number>();
        serie1.setName("Milch");
        serie1.getData().add(new XYChart.Data<String , Number>(j50, 321));
        serie1.getData().add(new XYChart.Data<String , Number>(j60, 35));
        serie1.getData().add(new XYChart.Data<String , Number>(j70, 0));
        serie1.getData().add(new XYChart.Data<String , Number>(j80, 0));
        serie1.getData().add(new XYChart.Data<String , Number>(j90, 0));
        serie1.getData().add(new XYChart.Data<String , Number>(j00, 0));
        serie1.getData().add(new XYChart.Data<String , Number>(j10, 0));

        XYChart.Series<String , Number> serie2 = new XYChart.Series<String
                    , Number>();
        serie2.setName("Kakao");
        serie2.getData().add(new XYChart.Data<String , Number>(j50, 0));
        serie2.getData().add(new XYChart.Data<String , Number>(j60, 18));
        serie2.getData().add(new XYChart.Data<String , Number>(j70, 32));
        serie2.getData().add(new XYChart.Data<String , Number>(j80, 0));
        serie2.getData().add(new XYChart.Data<String , Number>(j90, 0));
        serie2.getData().add(new XYChart.Data<String , Number>(j00, 0));
        serie2.getData().add(new XYChart.Data<String , Number>(j10, 0));

        XYChart.Series<String , Number> serie3 = new XYChart.Series<String
                    , Number>();
        serie3.setName("Wasser");
        serie3.getData().add(new XYChart.Data<String , Number>(j50, 0));
        serie3.getData().add(new XYChart.Data<String , Number>(j60, 10));
        serie3.getData().add(new XYChart.Data<String , Number>(j70, 134));
        serie3.getData().add(new XYChart.Data<String , Number>(j80, 160));
        serie3.getData().add(new XYChart.Data<String , Number>(j90, 135));
        serie3.getData().add(new XYChart.Data<String , Number>(j00, 146));
        serie3.getData().add(new XYChart.Data<String , Number>(j10, 120));

        XYChart.Series<String , Number> serie4 = new XYChart.Series<String
                    , Number>();
        serie4.setName("Cola");
        serie4.getData().add(new XYChart.Data<String , Number>(j50, 0));
        serie4.getData().add(new XYChart.Data<String , Number>(j60, 0));
        serie4.getData().add(new XYChart.Data<String , Number>(j70, 9));
        serie4.getData().add(new XYChart.Data<String , Number>(j80, 3));
        serie4.getData().add(new XYChart.Data<String , Number>(j90, 0));
        serie4.getData().add(new XYChart.Data<String , Number>(j00, 0));
        serie4.getData().add(new XYChart.Data<String , Number>(j10, 0));
        
        XYChart.Series<String , Number> serie5 = new XYChart.Series<String
                    , Number>();
        serie5.setName("Limo");
        serie5.getData().add(new XYChart.Data<String , Number>(j50, 0));
        serie5.getData().add(new XYChart.Data<String , Number>(j60, 0));
        serie5.getData().add(new XYChart.Data<String , Number>(j70, 29));
        serie5.getData().add(new XYChart.Data<String , Number>(j80, 2));
        serie5.getData().add(new XYChart.Data<String , Number>(j90, 1));
        serie5.getData().add(new XYChart.Data<String , Number>(j00, 1));
        serie5.getData().add(new XYChart.Data<String , Number>(j10, 0));
        
        XYChart.Series<String , Number> serie6 = new XYChart.Series<String
                    , Number>();
        serie6.setName("Bier");
        serie6.getData().add(new XYChart.Data<String , Number>(j50, 0));
        serie6.getData().add(new XYChart.Data<String , Number>(j60, 0));
        serie6.getData().add(new XYChart.Data<String , Number>(j70, 19));
        serie6.getData().add(new XYChart.Data<String , Number>(j80, 39));
        serie6.getData().add(new XYChart.Data<String , Number>(j90, 42));
        serie6.getData().add(new XYChart.Data<String , Number>(j00, 76));
        serie6.getData().add(new XYChart.Data<String , Number>(j10, 67));
        
        XYChart.Series<String , Number> serie7 = new XYChart.Series<String
                    , Number>();
        serie7.setName("Wein");
        serie7.getData().add(new XYChart.Data<String , Number>(j50, 0));
        serie7.getData().add(new XYChart.Data<String , Number>(j60, 0));
        serie7.getData().add(new XYChart.Data<String , Number>(j70, 5));
        serie7.getData().add(new XYChart.Data<String , Number>(j80, 34));
        serie7.getData().add(new XYChart.Data<String , Number>(j90, 46));
        serie7.getData().add(new XYChart.Data<String , Number>(j00, 76));
        serie7.getData().add(new XYChart.Data<String , Number>(j10, 68));
                                                
        Scene scene = new Scene(bc, 800, 600);
        String stylesheet = getClass().getResource("/styles/styles.css").toExternalForm();
        scene.getStylesheets().add(stylesheet);
        bc.getData().addAll(serie1, serie2, serie3, serie4, serie5, serie6, serie7);
        stage.setScene(scene);
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

In Zeile 107 und 108 werden zur Farbkonfiguration die folgenden Stylesheets aus einer Datei geladen und an die scene übergeben. Die erste Angabe ist hierbei für die Hintergrundfarbe der Grafik und der Legende verantwortlich.

.chart-plot-background, .chart, .chart-legend { -fx-background-color: #bbb; }
/* Milch */
.default-color0.chart-bar-symbol { -fx-bar-fill: #ffffff; }
.default-color0.chart-bar { -fx-bar-fill: #ffffff; }
/* Kakao */
.default-color1.chart-bar-symbol { -fx-bar-fill: #932; }
.default-color1.chart-bar { -fx-bar-fill: #432; }
/* Wasser */
.default-color2.chart-bar-symbol { -fx-bar-fill: #0af; }
.default-color2.chart-bar { -fx-bar-fill: #0af; }
/* Cola */
.default-color3.chart-bar-symbol { -fx-bar-fill: #855; }
.default-color3.chart-bar { -fx-bar-fill: #855; }
/* Limo */
.default-color4.chart-bar-symbol { -fx-bar-fill: #fd0; }
.default-color4.chart-bar { -fx-bar-fill: #fd0; }
/* Bier */
.default-color5.chart-bar-symbol { -fx-bar-fill: #d9c033; }
.default-color5.chart-bar { -fx-bar-fill: #d9c033; }
/* Wein */
.default-color6.chart-bar-symbol { -fx-bar-fill: #fd8; }
.default-color6.chart-bar { -fx-bar-fill: #fd8; }
Beispiel zur Farkonfiguratiion eines Säulendiagramms mit mehreren Datengruppen
Quellen
  1. https://de.wikipedia.org/wiki/Liste_der_BMW-Motorräder
  2. https://docs.oracle.com/javase/8/javafx/user-interface-tutorial/bar-chart.htm#CIHJFHDE