Reputation: 23
I'm using XYChart
in JavaFX 8 and I would like to display gaps for empty cells for the specified series. When I passed null value then I got NullPointerException
:
series.get(index).getData().add(new XYChart.Data<>(Key, null));
I also found the bug https://bugs.openjdk.java.net/browse/JDK-8092134 describing this problem, but I don't know is it still actual.
Does anyone know how to resolve this problem?
Best regards,
Michael
Upvotes: 1
Views: 344
Reputation: 10009
As it is quite evident that, this feature is not included.. and if you are very desperate to get this behavior, you can try the below logic.
Having said that, there can be many better ways, but this answer is to give you some initial idea about how you can tweak the current implementation using the protected methods of the chart.
The idea is.. once the plot children layout is done, we recompute the logic of rendering the line path.. and remove the unwanted data points. And as mentioned, this is just for idea purpose only, if you have more data series, then you may need to work accordingly.
[UPDATE] : If you want the paths/data points for each series, append the ".series" to the ".chart-series-line" and ".data" style classes.
Please check the below demo:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.*;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.layout.VBox;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.stage.Stage;
import java.util.*;
public class XYChartDemo extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
VBox root = new VBox();
CategoryAxis xAxis = new CategoryAxis();
xAxis.setLabel("days");
NumberAxis yAxis = new NumberAxis();
yAxis.setLabel("USD");
// AS OF NOW THIS IS THE CORE POINT I AM RELYING ON !! YOU CAN THINK OF A BETTER APPROACH TO IDENTIFY THE GAP POINTS.
List<Integer> s0GapIndexes = Arrays.asList(3, 7);
List<Integer> s1GapIndexes = Arrays.asList(4, 8);
Map<Integer, List<Integer>> seriesGap = new HashMap<>();
seriesGap.put(0, s0GapIndexes);
seriesGap.put(1, s1GapIndexes);
XYChart.Series<String, Double> series0 = new XYChart.Series<>();
series0.getData().add(new Data<>("2000-01-01", 13.2));
series0.getData().add(new Data<>("2000-01-02", 10.1));
series0.getData().add(new Data<>("2000-01-03", 14.1));
series0.getData().add(new Data<>("2000-01-04", 0.0)); // gap (INDEX 3)
series0.getData().add(new Data<>("2000-01-05", 6.3));
series0.getData().add(new Data<>("2000-01-06", 9.82));
series0.getData().add(new Data<>("2000-01-07", 12.82));
series0.getData().add(new Data<>("2000-01-08", 0.0)); // gap (INDEX 7)
series0.getData().add(new Data<>("2000-01-09", 4.82));
series0.getData().add(new Data<>("2000-01-10", 8.82));
series0.getData().add(new Data<>("2000-01-11", 8.82));
XYChart.Series<String, Double> series1 = new XYChart.Series<>();
series1.getData().add(new Data<>("2000-01-01", 20.2));
series1.getData().add(new Data<>("2000-01-02", 14.1));
series1.getData().add(new Data<>("2000-01-03", 7.1));
series1.getData().add(new Data<>("2000-01-04", 9.0));
series1.getData().add(new Data<>("2000-01-05", 0.0)); // gap (INDEX 4)
series1.getData().add(new Data<>("2000-01-06", 5.32));
series1.getData().add(new Data<>("2000-01-07", 11.0));
series1.getData().add(new Data<>("2000-01-08", 15.3));
series1.getData().add(new Data<>("2000-01-09", 0.0)); // gap (INDEX 8)
series1.getData().add(new Data<>("2000-01-10", 4.82));
series1.getData().add(new Data<>("2000-01-11", 6.82));
CustomLineChart lineChart = new CustomLineChart(xAxis, yAxis, seriesGap);
lineChart.getData().addAll(series0, series1);
root.getChildren().addAll(lineChart);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
class CustomLineChart<X, Y> extends LineChart<X, Y> {
Map<Integer, List<Integer>> seriesGap;
public CustomLineChart(Axis<X> xAxis, Axis<Y> yAxis, Map<Integer, List<Integer>> seriesGap) {
super(xAxis, yAxis);
this.seriesGap = seriesGap;
}
@Override
protected void layoutPlotChildren() {
super.layoutPlotChildren();
updatePath();
updateDataPoints();
}
private void updatePath() {
seriesGap.forEach((seriesNo, gapIndexes) -> {
Path path = (Path) lookup(".chart-series-line.series" + seriesNo);
System.out.println(path);
if (!path.getElements().isEmpty()) {
int dataSize = getData().get(seriesNo).getData().size();
int pathEleSize = path.getElements().size();
// Just ensuring we are dealing with right path
if (pathEleSize == dataSize + 1) {
// Build a new path, by jumping the gap points
List<PathElement> newPath = new ArrayList<>();
newPath.add(path.getElements().get(0));
for (int i = 1; i < path.getElements().size(); i++) {
if (gapIndexes.contains(i - 1)) {
LineTo lt = (LineTo) path.getElements().get(i + 1);
newPath.add(new MoveTo(lt.getX(), lt.getY()));
} else {
newPath.add(path.getElements().get(i));
}
}
// Update the new path to the current path.
path.getElements().clear();
path.getElements().addAll(newPath);
}
}
});
}
private void updateDataPoints() {
Group plotContent = (Group) lookup(".plot-content");
seriesGap.forEach((seriesNo, gapIndexes) -> {
// Remove all data points at the gap indexes
gapIndexes.forEach(i -> {
Node n = lookup(".series" + seriesNo + ".data" + i);
if (n != null) {
plotContent.getChildren().remove(n);
}
});
});
}
}
}
Upvotes: 3