Issue
I want to add a Line between each Category on a CategoryAxis, the Problem is that the Categories change based on User Input, so i would need to add them at runtime, which I am doing by extending CustomBarChart and adding them on seriesAdded().
How can I get get the Position of each Category (xAxis) aswell as the Length of each Category (xAxis) so i can calculate where to put the Line between the Graphs.
Haven't found a way to get the Width of each Category yet.
Using the getDisplayPosition() should get me the Position but using it for this Graph e.g. gave me these results and I can't seem to figure out how they're related to the actual Position.
4.545454545454546
3.6363636363636367
2.7272727272727275
1.8181818181818188
0.9090909090909096
8.881784197001252E-16
-0.9090909090909083
-1.8181818181818175
-2.7272727272727266
-3.636363636363636
-4.545454545454544
Solution
This is an adaptation of the idea here. It should provide a good starting point.
package org.jamesd.examples.barchartwithlines;
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.scene.layout.BorderPane;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import java.io.IOException;
import java.time.Month;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
CategoryAxis xAxis = new CategoryAxis();
NumberAxis yAxis = new NumberAxis();
BarChart<String, Number> chart = new BarChart<>(xAxis, yAxis) {
private List<Line> lines = new ArrayList<>();
@Override
protected void layoutPlotChildren() {
super.layoutPlotChildren();
List<Series<String, Number>> data = getData();
if (data.size() == 0) {
getPlotChildren().removeAll(lines);
lines.clear();
return ;
}
// assuming only one series
Series<String, Number> series = data.get(0);
int numLines = series.getData().size() - 1;
// remove unused lines:
for (int i = lines.size() - 1 ; i >= numLines ; i--) {
getPlotChildren().remove(lines.get(i));
lines.remove(i);
}
// create new lines:
for (int i = lines.size() ; i < numLines ; i++) {
Line line = new Line();
lines.add(line);
getPlotChildren().add(line);
}
double[] catPositions = new double[numLines + 1];
for (int i = 0 ; i < catPositions.length ; i++) {
Data<String, Number> d = series.getData().get(i);
catPositions[i] = xAxis.getDisplayPosition(d.getXValue());
}
Arrays.sort(catPositions);
double startY = yAxis.getDisplayPosition(yAxis.getLowerBound());
double endY = yAxis.getDisplayPosition(yAxis.getUpperBound());
for (int i = 0 ; i < catPositions.length - 1 ; i++) {
Line line = lines.get(i);
double lineX = (catPositions[i] + catPositions[i+1]) / 2 ;
line.setStartX(lineX);
line.setEndX(lineX);
line.setStartY(startY);
line.setEndY(endY);
}
}
};
Random rng = new Random();
XYChart.Series<String, Number> series = new XYChart.Series<>();
DateTimeFormatter format = DateTimeFormatter.ofPattern("LLL");
for (Month month : Month.values()) {
series.getData().add(new XYChart.Data<>(format.format(month), rng.nextDouble() * 100));
}
series.setName("2020");
chart.getData().add(series);
BorderPane root = new BorderPane(chart);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
This results in
Answered By - James_D
Answer Checked By - Dawn Plyler (JavaFixing Volunteer)