JavaFX LineChart legend style

2019-01-27 06:07发布

问题:

I want to update the style of LineChart legend, i using setStyle on the nodes with the correspoding series class.

String color = ....
XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>();
_chart.getData().add(series);

String seriesClass = null;
for(String styleClass : series.getNode().getStyleClass())
{
    if(styleClass.startsWith("series"))
    {
        seriesClass = styleClass;
        break;
    }
}
if(seriesClass != null)
{
    //
    // Customize the style.
    //
    StringBuilder sb = new StringBuilder();
    sb.append("-fx-stroke: ");
    sb.append(color);
    sb.append("; ");
    sb.append("-fx-background-color: ");
    sb.append(color);
    sb.append(", white;");
    if(doted)
    {
        sb.append("-fx-stroke-dash-array: 10 10");
    }
    _styles.put(seriesClass, sb.toString()); 
}

java.util.Set<javafx.scene.Node> nodes = _chart.lookupAll("." + seriesClass);
for(javafx.scene.Node n : nodes)
{
    n.setStyle(style);
}

The thing is that this just affect the style of the path, the legend style doesn't change. I have printed the chart node children and see that legends are not fully created after add series call returns:

Legend@18e8627[styleClass=chart-legend]
    Label@1689c98[styleClass=label chart-legend-item]
    Label@100e4ce[styleClass=label chart-legend-item]
    Label@1adcb5e[styleClass=label chart-legend-item]
    Label@102a8fb[styleClass=label chart-legend-item]

A bit later if i print the children again:

Legend@9a095[styleClass=chart-legend]
    Label[id=null, styleClass=label chart-legend-item]
        LabelSkin[id=null, styleClass=label chart-legend-item]
            Region@12acafc[styleClass=chart-legend-item-symbol chart-line-symbol series0 default-color0]
            LabeledText@749a47[styleClass=text]
    Label[id=null, styleClass=label chart-legend-item]
        LabelSkin[id=null, styleClass=label chart-legend-item]
            Region@3ca3a4[styleClass=chart-legend-item-symbol chart-line-symbol series1 default-color1]
            LabeledText@11b9972[styleClass=text]
    Label[id=null, styleClass=label chart-legend-item]
        LabelSkin[id=null, styleClass=label chart-legend-item]
            Region@57f433[styleClass=chart-legend-item-symbol chart-line-symbol series2 default-color2]
            LabeledText@6172b5[styleClass=text]
    Label[id=null, styleClass=label chart-legend-item]
        LabelSkin[id=null, styleClass=label chart-legend-item]
            Region@16458ed[styleClass=chart-legend-item-symbol chart-line-symbol series3 default-color3]
            LabeledText@10a68bd[styleClass=text]

If i update the style now, the legend style update correctly.

How can i know when the Region child with classes needed for set the style were added, so i can setStyle on that nodes?

any other ideas to update the legend style when a new series has been added?

回答1:

I ran into this issue as well. Came up with a work-around that detects when the legend items are created so that dynamic styling can be added to them.

I added a ListChangeListener to the legend's "getChildrenUnmodifiable()" ObservableList, which in turn adds a ListChangeListener to each of the legend's children as they get added. From within this listener, we can tell when new items are being added to the legend (or removed). This allow us to then make the dynamic style changes.

for (Node n : lineChart.getChildrenUnmodifiable())
        {
            if (n instanceof Legend)
            {
                final Legend legend = (Legend) n;

                // remove the legend
                legend.getChildrenUnmodifiable().addListener(new ListChangeListener<Object>()
                {
                    @Override
                    public void onChanged(Change<?> arg0)
                    {
                        for (Node node : legend.getChildrenUnmodifiable())
                        {
                            if (node instanceof Label)
                            {
                                final Label label = (Label) node;
                                label.getChildrenUnmodifiable().addListener(new ListChangeListener<Object>()
                                {
                                    @Override
                                    public void onChanged(Change<?> arg0)
                                    {
                                        //make style changes here
                                    }

                                });
                            }
                        }
                    }
                });
            }
        }


回答2:

None of this worked for me so, I found another solution. Neither the answer nor the Platform.runLater method.

XYChart.Series<Number,Number> value  //is our serie value.

for(int index = 0; index<value.getData().size(); index++){
    // we're looping for each data point, changing the color of line symbol
    XYChart.Data dataPoint = value.getData().get(index);
    Node lineSymbol = dataPoint.getNode().lookup(".chart-line-symbol");
    lineSymbol.setStyle("-fx-background-color: #0000FF, white;");
}
// and this is for the color of the line
value.getNode().setStyle("-fx-border-style: solid; -fx-stroke: #0000FF; -fx-background-color: #0000FF;");

For Legend Color changing:

for(Node n : chart.getChildrenUnmodifiable()){
   if(n instanceof Legend){
      for(Legend.LegendItem legendItem : ((Legend)n).getItems()){
        legendItem.getSymbol().setStyle("-fx-background-color: #0000ff, white;");
      }
   }
}

Hope this works for anyone searching for this too.



回答3:

Know this is damn old, but perhaps someone still needs a smoother solution. Just use the Platform.runLater function:

int index = 2;
Platform.runLater(new Runnable() {
    @Override
    public void run() {
        myChart.lookupAll(".chart-legend-item-symbol").toArray()[index].setStyle("-fx-border-color: rgba(200,0,0,1)");
}});


标签: java javafx-2