Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8173985

StackedBarChart inside ScrollPane freeze when trying to scroll it

    Details

    • Subcomponent:
    • CPU:
      x86
    • OS:
      other

      Description

      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

        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              webbuggrp Webbug Group
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated: