2

I tried to implement this solution for StackedBar Chart but it turns out that there is no Java method getBarGap() in StackedBar chart. Is there any solution into the latest JavaFX version for this problem?

Basic example:

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedBarChart;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class MainApp extends Application
{

    private StackedBarChart<String, Number> stackedChart;
    private List<EventsObj> eventsObj;

    @Override
    public void start(Stage stage) throws Exception
    {
        createStackedChart();
        List<EventsObj> testData = generateTestData();

        addStackedChartData(testData);

        HBox hb = new HBox();
        hb.getChildren().add(stackedChart);

        Scene scene = new Scene(hb);
        stage.setTitle("JavaFX and Maven");
        stage.setScene(scene);
        stage.show();
    }

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

    private void createStackedChart()
    {
        CategoryAxis xAxis = new CategoryAxis();
        xAxis.setLabel("Days");
        NumberAxis yAxis = new NumberAxis();

        stackedChart = new StackedBarChart<>(xAxis, yAxis);
        stackedChart.setCategoryGap(20);

        stackedChart.widthProperty().addListener((obs, b, b1) ->
        {
            // Chart Bar column is not automatically resized. We need to wait for next JavaFX releases to fix this.
            Platform.runLater(() -> setMaxBarWidth(stackedChart, xAxis, 40, 10));
        });
    }

    private List<EventsObj> generateTestData()
    {
        eventsObj = new ArrayList<>();

        for (int i = 0; i < 5; i++)
        {
            eventsObj.add(new EventsObj(String.valueOf(randomDate()), random(2, 60), random(2, 60), random(2, 60), random(2, 60)));
        }

        return eventsObj;
    }

    public static int random(int lowerBound, int upperBound)
    {
        return (lowerBound + (int) Math.round(Math.random() * (upperBound - lowerBound)));
    }

    private LocalDate randomDate()
    {
        Random random = new Random();
        int minDay = (int) LocalDate.of(1900, 1, 1).toEpochDay();
        int maxDay = (int) LocalDate.of(2015, 1, 1).toEpochDay();
        long randomDay = minDay + random.nextInt(maxDay - minDay);

        LocalDate randomBirthDate = LocalDate.ofEpochDay(randomDay);

        return randomBirthDate;
    }

    private void addStackedChartData(List<EventsObj> data)
    {
        List<XYChart.Series<String, Number>> dataSeries = new ArrayList<>(data.size());

        for (EventsObj data1 : data)
        {
            final XYChart.Series<String, Number> series1 = new XYChart.Series<>();
            series1.setName(data1.getDate());
            series1.getData().setAll(
                new XYChart.Data<>("Info", data1.getInfo()));
            dataSeries.add(series1);
        }

        stackedChart.getData().setAll(dataSeries);
    }

    private void setMaxBarWidth(StackedBarChart<String, Number> bc, CategoryAxis xAxis, double maxBarWidth, double minCategoryGap)
    {
        double barWidth = 0;
        do
        {
            double catSpace = xAxis.getCategorySpacing();
            double avilableBarSpace = catSpace - (bc.getCategoryGap() + bc.getCategoryGap());
            barWidth = (avilableBarSpace / bc.getData().size()) - bc.getCategoryGap();
            if (barWidth > maxBarWidth)
            {
                avilableBarSpace = (maxBarWidth + bc.getCategoryGap()) * bc.getData().size();
                bc.setCategoryGap(catSpace - avilableBarSpace - bc.getCategoryGap());
            }
        }
        while (barWidth > maxBarWidth);

        do
        {
            double catSpace = xAxis.getCategorySpacing();
            double avilableBarSpace = catSpace - (minCategoryGap + bc.getCategoryGap());
            barWidth = Math.min(maxBarWidth, (avilableBarSpace / bc.getData().size()) - bc.getCategoryGap());
            avilableBarSpace = (barWidth + bc.getCategoryGap()) * bc.getData().size();
            bc.setCategoryGap(catSpace - avilableBarSpace - bc.getCategoryGap());
        }
        while (barWidth < maxBarWidth && bc.getCategoryGap() > minCategoryGap);
    }
}
Community
  • 1
  • 1
Peter Penzov
  • 1,126
  • 134
  • 430
  • 808
  • Use [`getCategoryGap`](http://docs.oracle.com/javase/8/javafx/api/javafx/scene/chart/StackedBarChart.html#categoryGapProperty) – James_D Jan 02 '16 at 22:44
  • I already tried but there is no result. – Peter Penzov Jan 02 '16 at 23:06
  • 'Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself.' – James_D Jan 02 '16 at 23:12

1 Answers1

3

In a BarChart chart, the different series added are plotted in the different columns from the same category with a barGap, some spacing in between.

But in a StackBarChart chart, the different series are plotted stacked in the same column, hence in this case there is no bar gap.

If you take the setMaxBarWidth() method from this answer, all you need to do is set bc.getBarGap() to 0, and take sbc.getData().size() as 1. In this case there is no need for iterations.

This will be the new method, really simplified:

private void setMaxCategoryWidth(double maxCategoryWidth, double minCategoryGap){
    double catSpace = xAxis.getCategorySpacing();
    sbc.setCategoryGap(catSpace - Math.min(maxCategoryWidth, catSpace - minCategoryGap));
}

Basically, all you have to do is subtract the desired width of your category from the initial width of the category and put this extra space into the categoryGap.

Now, you can create your chart:

StackedBarChart<String, Number> sbc =  new StackedBarChart<>(xAxis, yAxis);

And set your desired size for the categories:

setMaxCategoryWidth(40, 10);
sbc.widthProperty().addListener((obs, b, b1) -> {
    Platform.runLater(() -> setMaxCategoryWidth(40, 10));
});

This pic is generated based on the sample 7.4 taken from here.

Size 40

Community
  • 1
  • 1
José Pereda
  • 44,311
  • 7
  • 104
  • 132
  • Thank you fir the excellent answer. Do you know in future is there going to be fix for this issue? – Peter Penzov Jan 10 '16 at 09:13
  • I don't think there will be many improvements in the charts API. Anyway, the issue should be reported in the first place... – José Pereda Jan 10 '16 at 09:52
  • How can you say that JavaFX will be discontinued !!?? You follow the mailing list, right? You know their [plans](http://mail.openjdk.java.net/pipermail/openjfx-dev/2015-December/018430.html) for JavaFX 9. – José Pereda Jan 10 '16 at 10:24