2

I have an application that displays several Line Charts with several Series like this:

enter image description here

I'd like to change the color of each Series but haven't found a way to achieve this. The only thing I found is how to change the default colors but that doesn't solve my problem.

Is there really now way to achieve individual colors for chart series?

Slaw
  • 37,820
  • 8
  • 53
  • 80
Wewius
  • 87
  • 7
  • Set the color [via CSS](https://openjfx.io/javadoc/19/javafx.graphics/javafx/scene/doc-files/cssref.html#linechart). To target a specific chart, give the chart an ID. – Slaw Feb 14 '23 at 01:59
  • Depends on the version of JavaFX, the preferable way is to use CSS – DVN-Anakin Feb 14 '23 at 02:00
  • @Slaw Thank you. This looks promising and I already have a CSS file but I'm not sure how to implement/use these style classes to get what I want. `chart-series-line series default-color` seems to only change what series gets what default color. This would lead to the first series of each Line Chart still getting the same color, would it not? – Wewius Feb 14 '23 at 02:09
  • 1
    Oracle have an [old tutorial for styling charts](https://docs.oracle.com/javafx/2/charts/css-styles.htm). It might be a bit out of date, but still mostly relevant. – jewelsea Feb 14 '23 at 03:15

1 Answers1

3

The JavaFX CSS Reference Guide says the following for LineChart:

Style class Comments Properties
"chart-series-line series<i> default-color<j>" Where <i> is the index of the series and <j> is the series’ color index Node
"chart-line-symbol series<i> data<j> default-color<k>" Where <i> is the index of the series, <j> is the index of the data within the series, and <k> is the series’ color index Node
"chart-line-symbol series<i> default-color<j>" Where <i> is the index of the series and <j> is the series’ color index LegendItem

Note: Although the line is only documented as a Node, by default it is actually a javafx.scene.shape.Path.

If you want to target a specific series' line, use .chart-series-line.series<i>, where <i> is replaced with the index of the series in the chart's data. And if you want to give a series of a specific chart a certain color, then simply give the chart an ID and use that in the CSS selector.

Here's an example. It uses so-called looked-up colors, which makes the CSS a little more scalable and organized. Also, they can have their values changed via the setStyle method in code, allowing you to dynamically change the color programmatically. Another approach for that is to use a "data URL", showcased in this other Stack Overflow answer.

style.css:

/*
 * The '-fx-stroke' is used to set the color of the line. The line is
 * targeted by the '.chart-series-line.series<i>' selectors.
 *
 * The '-fx-background-color' is used to set the color of the legend
 * symbol so it matches the line. This would also color the symbols
 * on the line if they were shown. These symbol nodes are targeted
 * by the '.chart-line-symbol.series<i>' selectors.
 *
 * Both the '-fx-series0-color' and '-fx-series1-color' "properties"
 * are looked-up colors. You can change the value of a looked-up color
 * in code by calling 'setStyle(...)' on the appropriate node.
 */

 #firstChart {
    -fx-series0-color: magenta;
    -fx-series1-color: dodgerblue;
 }

#secondChart {
    -fx-series0-color: red;
    -fx-series1-color: black;
}

#firstChart .chart-series-line.series0,
#firstChart .chart-line-symbol.series0 {
    -fx-stroke: -fx-series0-color;
    -fx-background-color: -fx-series0-color;
}

#firstChart .chart-series-line.series1,
#firstChart .chart-line-symbol.series1 {
    -fx-stroke: -fx-series1-color;
    -fx-background-color: -fx-series1-color;
}

#secondChart .chart-series-line.series0,
#secondChart .chart-line-symbol.series0 {
    -fx-stroke: -fx-series0-color;
    -fx-background-color: -fx-series0-color;
}

#secondChart .chart-series-line.series1,
#secondChart .chart-line-symbol.series1 {
    -fx-stroke: -fx-series1-color;
    -fx-background-color: -fx-series1-color;
}

Main.java:

import java.util.function.UnaryOperator;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) {
        var chart1 = createLineChart("Chart 1");
        chart1.setId("firstChart");
        chart1.getData().add(createSeries("f(x) = x", x -> x));
        chart1.getData().add(createSeries("f(x) = 2x", x -> 2 * x));

        /*
         * Uncomment the line of code below to demonstrate dynamically changing
         * the value of a looked-up color.
         */
        // chart1.setStyle("-fx-series0-color: goldenrod;");

        var chart2 = createLineChart("Chart 2");
        chart2.setId("secondChart");
        chart2.getData().add(createSeries("f(x) = x^2", x -> x * x));
        chart2.getData().add(createSeries("f(x) = x^3", x -> x * x * x));

        var root = new VBox(10, chart1, chart2);
        root.setPadding(new Insets(10));

        primaryStage.setScene(new Scene(root, 1000, 720));
        primaryStage.getScene()
                .getStylesheets()
                .add(Main.class.getResource("/style.css").toString());
        primaryStage.show();
    }

    private LineChart<Number, Number> createLineChart(String title) {
        var chart = new LineChart<>(new NumberAxis(), new NumberAxis());
        chart.setTitle(title);
        chart.getXAxis().setLabel("x");
        chart.getYAxis().setLabel("f(x)");
        chart.setCreateSymbols(false);
        return chart;
    }

    private XYChart.Series<Number, Number> createSeries(
            String name, 
            UnaryOperator<Double> func) {
        var series = new XYChart.Series<Number, Number>();
        series.setName(name);

        for (int x = 0; x < 20; x++) {
            series.getData().add(new XYChart.Data<>(x, func.apply((double) x)));
        }

        return series;
    }

}
Slaw
  • 37,820
  • 8
  • 53
  • 80
  • Also, if you need to dynamically set the colors in code, you can use a CSS data url, kind of like [this answer](https://stackoverflow.com/a/74074598/1155209). – jewelsea Feb 14 '23 at 03:10
  • @jewelsea Nice! Didn't know that was possible. I've always implemented that by using looked-up colors. I've edited my answer to show that approach and to link to your answer for the data URL approach. – Slaw Feb 14 '23 at 03:22
  • Yeah the data urls work well with [string formatters](https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/String.html#formatted(java.lang.Object...)) for dynamically setting values in css when looked up colors aren’t enough. It will work well with [string templates](https://openjdk.org/jeps/430) when that feature is released. IDEs like idea can recognize the language type of multi-line strings used in a data url and apply css specific formatting, parsing and highlighting. – jewelsea Feb 14 '23 at 04:12