FULL PRODUCT VERSION :
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.10586]
EXTRA RELEVANT SYSTEM CONFIGURATION :
Intel core i5-6600 CPU @ 3.30GHz
16.0 GB RAM
Intel HD graphics 530
A DESCRIPTION OF THE PROBLEM :
We need to display a StackedBarChart with more than 100 bars. Chart wrapped in a ScrollPane for the possibility scroll through the X-axis (CategoryAxis). When adding a large amounts of data (approximately 100 bars on the screen), the UI begin to freeze. (See attached Source Code to reproduce the problem).
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1) Run attached source code.
2) Click 'Fill data' button
3) Try to scroll chart.
The more data, the more freezes.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Scrolling without any freezes.
ACTUAL -
Strong UI freeze when trying to scroll the chart
ERROR MESSAGES/STACK TRACES THAT OCCUR :
No crash appears, it is a performance issue.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
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.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class StackedBarChartSample {
static XYChart.Series<String, Number> series1;
static XYChart.Series<String, Number> series2;
static XYChart.Series<String, Number> series3;
static StackedBarChart sbc;
static CategoryAxis xAxis;
private static final int BAR_WIDTH = 40;
private static final int BAR_MARGIN = 40;
private static ScrollPane scrollPane;
private static void initChart() {
Stage stage = new Stage();
stage.setTitle("Bar Chart Sample");
xAxis = new CategoryAxis();
xAxis.setStartMargin(0);
xAxis.setEndMargin(500);
NumberAxis yAxis = new NumberAxis();
xAxis.setLabel("Value X");
yAxis.setLabel("Value Y");
sbc = new StackedBarChart(xAxis, yAxis);
sbc.setLegendVisible(true);
series1 = new XYChart.Series<>();
series2 = new XYChart.Series<>();
series3 = new XYChart.Series<>();
xAxis.setTickMarkVisible(true);
xAxis.setTickLabelsVisible(true);
sbc.getData().addAll(series1, series2, series3);
sbc.setAnimated(false);
yAxis.setAnimated(false);
xAxis.setAnimated(false);
sbc.setCache(true);
xAxis.categorySpacingProperty().addListener((observable, oldValue, newValue) -> setMaxCategoryWidth());
scrollPane = new ScrollPane();
scrollPane.setContent(sbc);
scrollPane.setFitToHeight(true);
HBox toolbar = new HBox();
TextField field = new TextField("200");
Button clear = new Button("Clear");
clear.setOnAction(event -> clearChart());
Button fill = new Button("Fill data");
fill.setOnAction(event -> fillData(field.getText()));
toolbar.getChildren().addAll(clear, field, fill);
BorderPane pane = new BorderPane();
pane.setBottom(toolbar);
pane.setCenter(scrollPane);
Scene scene = new Scene(pane, 500, 500);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
new JFXPanel();
Platform.runLater(() -> initChart());
}
private static void clearChart() {
scrollPane.setFitToWidth(true);
sbc.getData().remove(0, sbc.getData().size());
series1 = new XYChart.Series<>();
series2 = new XYChart.Series<>();
series3 = new XYChart.Series<>();
sbc.getData().addAll(series1, series2, series3);
}
private static void fillData(String count) {
for (int i = 0; i < Integer.parseInt(count); i++) {
String name = new Double(Math.random()).toString();
series1.getData().add(new XYChart.Data<>(name, Math.random()));
series2.getData().add(new XYChart.Data<>(name, Math.random()));
series3.getData().add(new XYChart.Data<>(name, Math.random()));
}
}
private static void setMaxCategoryWidth() {
Platform.runLater(() -> {
double catSpace = xAxis.getCategorySpacing();
if ((catSpace - sbc.getCategoryGap()) < BAR_WIDTH && !isChartEmpty() && ((sbc.getWidth() / series1.getData().size()) * 0.8 < BAR_WIDTH + BAR_MARGIN)) {
scrollPane.setFitToWidth(false);
sbc.setPrefWidth(sbc.getWidth() + 200);
}
double gap = catSpace - Math.min(BAR_WIDTH, catSpace - BAR_MARGIN);
if (!Double.isNaN(gap) && !Double.isInfinite(gap) && gap > 0) {
sbc.setCategoryGap(gap);
}
});
}
static boolean isChartEmpty() {
return series1.getData().isEmpty() && series2.getData().isEmpty() && series3.getData().isEmpty();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Decreasing the minimum width of the bar allows increase the amount of data that you can dispaly and scroll without freezing
java version "1.8.0_112"
Java(TM) SE Runtime Environment (build 1.8.0_112-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.112-b15, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.10586]
EXTRA RELEVANT SYSTEM CONFIGURATION :
Intel core i5-6600 CPU @ 3.30GHz
16.0 GB RAM
Intel HD graphics 530
A DESCRIPTION OF THE PROBLEM :
We need to display a StackedBarChart with more than 100 bars. Chart wrapped in a ScrollPane for the possibility scroll through the X-axis (CategoryAxis). When adding a large amounts of data (approximately 100 bars on the screen), the UI begin to freeze. (See attached Source Code to reproduce the problem).
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1) Run attached source code.
2) Click 'Fill data' button
3) Try to scroll chart.
The more data, the more freezes.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Scrolling without any freezes.
ACTUAL -
Strong UI freeze when trying to scroll the chart
ERROR MESSAGES/STACK TRACES THAT OCCUR :
No crash appears, it is a performance issue.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
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.control.Button;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class StackedBarChartSample {
static XYChart.Series<String, Number> series1;
static XYChart.Series<String, Number> series2;
static XYChart.Series<String, Number> series3;
static StackedBarChart sbc;
static CategoryAxis xAxis;
private static final int BAR_WIDTH = 40;
private static final int BAR_MARGIN = 40;
private static ScrollPane scrollPane;
private static void initChart() {
Stage stage = new Stage();
stage.setTitle("Bar Chart Sample");
xAxis = new CategoryAxis();
xAxis.setStartMargin(0);
xAxis.setEndMargin(500);
NumberAxis yAxis = new NumberAxis();
xAxis.setLabel("Value X");
yAxis.setLabel("Value Y");
sbc = new StackedBarChart(xAxis, yAxis);
sbc.setLegendVisible(true);
series1 = new XYChart.Series<>();
series2 = new XYChart.Series<>();
series3 = new XYChart.Series<>();
xAxis.setTickMarkVisible(true);
xAxis.setTickLabelsVisible(true);
sbc.getData().addAll(series1, series2, series3);
sbc.setAnimated(false);
yAxis.setAnimated(false);
xAxis.setAnimated(false);
sbc.setCache(true);
xAxis.categorySpacingProperty().addListener((observable, oldValue, newValue) -> setMaxCategoryWidth());
scrollPane = new ScrollPane();
scrollPane.setContent(sbc);
scrollPane.setFitToHeight(true);
HBox toolbar = new HBox();
TextField field = new TextField("200");
Button clear = new Button("Clear");
clear.setOnAction(event -> clearChart());
Button fill = new Button("Fill data");
fill.setOnAction(event -> fillData(field.getText()));
toolbar.getChildren().addAll(clear, field, fill);
BorderPane pane = new BorderPane();
pane.setBottom(toolbar);
pane.setCenter(scrollPane);
Scene scene = new Scene(pane, 500, 500);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
new JFXPanel();
Platform.runLater(() -> initChart());
}
private static void clearChart() {
scrollPane.setFitToWidth(true);
sbc.getData().remove(0, sbc.getData().size());
series1 = new XYChart.Series<>();
series2 = new XYChart.Series<>();
series3 = new XYChart.Series<>();
sbc.getData().addAll(series1, series2, series3);
}
private static void fillData(String count) {
for (int i = 0; i < Integer.parseInt(count); i++) {
String name = new Double(Math.random()).toString();
series1.getData().add(new XYChart.Data<>(name, Math.random()));
series2.getData().add(new XYChart.Data<>(name, Math.random()));
series3.getData().add(new XYChart.Data<>(name, Math.random()));
}
}
private static void setMaxCategoryWidth() {
Platform.runLater(() -> {
double catSpace = xAxis.getCategorySpacing();
if ((catSpace - sbc.getCategoryGap()) < BAR_WIDTH && !isChartEmpty() && ((sbc.getWidth() / series1.getData().size()) * 0.8 < BAR_WIDTH + BAR_MARGIN)) {
scrollPane.setFitToWidth(false);
sbc.setPrefWidth(sbc.getWidth() + 200);
}
double gap = catSpace - Math.min(BAR_WIDTH, catSpace - BAR_MARGIN);
if (!Double.isNaN(gap) && !Double.isInfinite(gap) && gap > 0) {
sbc.setCategoryGap(gap);
}
});
}
static boolean isChartEmpty() {
return series1.getData().isEmpty() && series2.getData().isEmpty() && series3.getData().isEmpty();
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Decreasing the minimum width of the bar allows increase the amount of data that you can dispaly and scroll without freezing