Set Bar Chart column width size
Asked Answered
P

3

5

Is there a way to set JavaFX Bar Chart column width size?

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 BarChartSample extends Application {
    final static String austria = "Austria";
    final static String brazil = "Brazil";
    final static String france = "France";
    final static String italy = "Italy";
    final static String usa = "USA";

    @Override public void start(Stage stage) {
        stage.setTitle("Bar Chart Sample");
        final CategoryAxis xAxis = new CategoryAxis();
        final NumberAxis yAxis = new NumberAxis();
        final BarChart<String,Number> bc = 
            new BarChart<String,Number>(xAxis,yAxis);
        bc.setTitle("Country Summary");
        xAxis.setLabel("Country");       
        yAxis.setLabel("Value");

        XYChart.Series series1 = new XYChart.Series();
        series1.setName("2003");       
        series1.getData().add(new XYChart.Data(austria, 25601.34));
        series1.getData().add(new XYChart.Data(brazil, 20148.82));
        series1.getData().add(new XYChart.Data(france, 10000));
        series1.getData().add(new XYChart.Data(italy, 35407.15));
        series1.getData().add(new XYChart.Data(usa, 12000));      

        XYChart.Series series2 = new XYChart.Series();
        series2.setName("2004");
        series2.getData().add(new XYChart.Data(austria, 57401.85));
        series2.getData().add(new XYChart.Data(brazil, 41941.19));
        series2.getData().add(new XYChart.Data(france, 45263.37));
        series2.getData().add(new XYChart.Data(italy, 117320.16));
        series2.getData().add(new XYChart.Data(usa, 14845.27));  

        XYChart.Series series3 = new XYChart.Series();
        series3.setName("2005");
        series3.getData().add(new XYChart.Data(austria, 45000.65));
        series3.getData().add(new XYChart.Data(brazil, 44835.76));
        series3.getData().add(new XYChart.Data(france, 18722.18));
        series3.getData().add(new XYChart.Data(italy, 17557.31));
        series3.getData().add(new XYChart.Data(usa, 92633.68));  

        Scene scene  = new Scene(bc,800,600);
        bc.getData().addAll(series1, series2, series3);
        stage.setScene(scene);
        stage.show();
    }

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

For example there is a way to setBarGap size but I can't find a way to set the maximum width of a Bar Chart column.

Is there a way to solve this?

Plexor answered 4/12, 2014 at 19:55 Comment(1)
I mean width. My mistake.Plexor
F
6

BarChart works on the basis that both barGap (gap between bars in the same category) and categoryGap (gap between bars in separate categories) can be set by the user, so for a given size of chart, the width of the bars is calculated internally every time a layout is requested.

To set a maximum width on every bar, we have to change either barGap or categoryGap accordingly. But we can do it programmatically, to respond to any change in the width of the chart.

Let's first calculate the bar width for an initial layout. According to BarChart.layoutPlotChildren():

double catSpace = xAxis.getCategorySpacing();
double avilableBarSpace = catSpace - (bc.getCategoryGap() + bc.getBarGap());
double barWidth = (avilableBarSpace / bc.getData().size()) - bc.getBarGap();

In the example provided by the OP, given the default gap values (barGap=4, categoryGap=10), the resulting barWidth is 37.4.

Let's assume we want to set a limit of 40, and a minimum category gap of 10:

double maxBarWidth=40;
double minCategoryGap=10;

If the chart's width increases when the scene is resized, we could limit barWidth by increasing categoryGap:

double barWidth=0;
do{
    double catSpace = xAxis.getCategorySpacing();
    double avilableBarSpace = catSpace - (bc.getCategoryGap() + bc.getBarGap());
    barWidth = (avilableBarSpace / bc.getData().size()) - bc.getBarGap();
    if(barWidth > maxBarWidth){
        avilableBarSpace=(maxBarWidth + bc.getBarGap())* bc.getData().size();
        bc.setCategoryGap(catSpace-avilableBarSpace-bc.getBarGap());
    }
} while(barWidth>maxBarWidth);

Note the do-while loop: as we modify categoryGap a new layout is performed and some initial values change. It takes usually two iterations to get the desired result.

We can repeat this operation as long as the chart's width keeps growing.

But if the chart's width starts decreasing, the gap between categories should be decreased too, while the bar width stays close to its maximum:

double barWidth=0;
do{
    double catSpace = xAxis.getCategorySpacing();
    double avilableBarSpace = catSpace - (minCategoryGap + bc.getBarGap());
    barWidth = Math.min(maxBarWidth, (avilableBarSpace / bc.getData().size()) - bc.getBarGap());
    avilableBarSpace=(barWidth + bc.getBarGap())* bc.getData().size();
    bc.setCategoryGap(catSpace-avilableBarSpace-bc.getBarGap());
} while(barWidth < maxBarWidth && bc.getCategoryGap()>minCategoryGap);

Putting everything together, and listening to scene width changes:

    ...
    Scene scene  = new Scene(bc,800,600);
    bc.getData().addAll(series1, series2, series3);
    stage.setScene(scene);
    stage.show();

    double maxBarWidth=40;
    double minCategoryGap=10;

    scene.widthProperty().addListener((obs,n,n1)->{
        if(bc.getData().size()==0) return;

        if(n!=null && (n1.doubleValue()>n.doubleValue())){
            double barWidth=0;
            do{
                double catSpace = xAxis.getCategorySpacing();
                double avilableBarSpace = catSpace - (bc.getCategoryGap() + bc.getBarGap());
                barWidth = (avilableBarSpace / bc.getData().size()) - bc.getBarGap();
                if (barWidth >maxBarWidth){
                    avilableBarSpace=(maxBarWidth + bc.getBarGap())* bc.getData().size();
                    bc.setCategoryGap(catSpace-avilableBarSpace-bc.getBarGap());
                }
            } while(barWidth>maxBarWidth);
        }

        if(n!=null && (n1.doubleValue()<n.doubleValue()) && bc.getCategoryGap()>minCategoryGap){
            double barWidth=0;
            do{
                double catSpace = xAxis.getCategorySpacing();
                double avilableBarSpace = catSpace - (minCategoryGap + bc.getBarGap());
                barWidth = Math.min(maxBarWidth, (avilableBarSpace / bc.getData().size()) - bc.getBarGap());
                avilableBarSpace=(barWidth + bc.getBarGap())* bc.getData().size();
                bc.setCategoryGap(catSpace-avilableBarSpace-bc.getBarGap());
            } while(barWidth < maxBarWidth && bc.getCategoryGap()>minCategoryGap);
        }
    });

Note that in the rest of the cases, barWidth will be calculated internally.

Fogbound answered 7/12, 2014 at 0:53 Comment(4)
do you know in Java 8u60 or 9 is there going to be setter for column width?Plexor
I'm not aware of that and there's no issue on JIRA about it.Kayleigh
This is not working for me. Barchart is not being displayed.After calling the setMaxBarWidth() method, barchart.setCategoryGap is being set to NaN.Ingeingeberg
to just make the columns wider barChart.setCategoryGap(0); barChart.setBarGap(1); sufficesApeman
C
2

Further to José Pereda's answer. You can set the width of the chart according to your data set. Together with the BarGap and CategoryGap setters, you can fine tune the width of the bars through calculation or trial and error. For instance, I did the following to get thinner bars. I set the width of an empty graph to 30 (for the axis and label??) and then added 100px to max and min width for every round in the game of cribbage (up to a maximum of the gridpane width) thus restricting the free space in the graph for the bars to fill.

 Pane barchart1 = new Pane;
 CategoryAxis xAxis = new CategoryAxis();
 NumberAxis yAxis = new NumberAxis(0,25,1);
 BarChart<String, Number> bc1 =
        new BarChart<String, Number>(xAxis, yAxis);
    // create Player chart
    bc1.setTitle(G.getPlayerName(0));
    bc1.setTitle(null);
    xAxis1.setLabel("Round");
    yAxis1.setLabel("Points");
    XYChart.Series series1 = new XYChart.Series();
    series1.setName("Pegging");
    XYChart.Series series3 = new XYChart.Series();
    series3.setName("Hand");
    XYChart.Series series5 = new XYChart.Series();
    series5.setName("Box");

    for (int i = 0; i < G.getRoundsSize(); i++) {
        series1.getData().add(new XYChart.Data(Integer.toString(i + 1), G.getRound(i).getPlayerRoundData(0).getPegPoints()));
        series3.getData().add(new XYChart.Data(Integer.toString(i + 1), G.getRound(i).getPlayerRoundData(0).getHandPoints()));
        series5.getData().add(new XYChart.Data(Integer.toString(i + 1), G.getRound(i).getPlayerRoundData(0).getBoxPoints()));
    }
    bc1.getData(). addAll(series1, series3, series5);
    bc1.setBarGap(0);
    bc1.setCategoryGap(1.0);
    bc1.setMaxHeight(180);
    bc1.setMinHeight(180);
    if(G.getRoundsSize() < 8){
        int wid = ((G.getRoundsSize() * 100) + 30);
        bc1.setMaxWidth(wid);
        bc1.setMinWidth(wid);
    } else {
        bc1.setMaxWidth(830);
        bc1.setMinWidth(830);
    }
    barchart1.getChildren().add(bc1);
Coolie answered 22/11, 2017 at 4:27 Comment(0)
C
0

Unfortunately you cannot simply set the bar width in the Javafx BarChart. You can only set the distance between the categories CategoryGap and the bars of a category BarGap.

Since the number of categories (categoryAxis.getCategories().size()), bars per category (barChart.getData().size()) and the available space for the axis (categoryAxis.getLayoutBounds().getWidth()) are known, the CategoryGap and BarGap can be simply calculated.

Important: This needs to be recalculated, if the chart is resized.

public static void setBarWidth(BarChart<?, ?> barChart, CategoryAxis categoryAxis, 
                               double barWidth, double availableSpace) {
    int dataSeriesCount = barChart.getData().size();
    int categoriesCount = categoryAxis.getCategories().size();

    if (dataSeriesCount <= 1) {
        barChart.setBarGap(0d);
    } else {
        barChart.setBarGap(5d);
    }

    double barWidthSum = barWidth * (categoriesCount * dataSeriesCount);
    double barGapSum = barChart.getBarGap() * (dataSeriesCount - 1);
    double categorySpacing = (availableSpace - barWidthSum - barGapSum) / categoriesCount;
    barChart.setCategoryGap(categorySpacing);
}
Cheyney answered 27/8, 2020 at 7:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.