Статьи

Столбчатые и точечные диаграммы JavaFX 2.0 (и StackedBarCharts JavaFX 2.1)

JavaFX 2.0 предоставляет встроенные возможности для генерации диаграмм, которые можно найти в пакете javafx.scene.chart . В этой статье я расскажу о создании гистограмм и точечной диаграммы с использованием JavaFX 2.0. В течение этого поста я использую Guava и некоторые функции Java 7 по пути.

Перед демонстрацией API диаграмм JavaFX 2.0 в этом первом листинге кода показана конфигурация данных, которые будут использоваться в примерах. В более реалистичном сценарии я получил бы эти данные из хранилища данных, но в этом случае я просто включил бы непосредственно в исходный код для удобного доступа к примеру. Хотя этот код сам по себе не связан с графиками JavaFX 2.0, в нем есть несколько интересных вещей. В коде используются подчеркивания Java 7 в числовых литералах, чтобы упростить чтение размера земель и совокупности состояний выборки, используемых в данных. В листинге кода также используется класс Gumm ImmutableMap (описанный в предыдущем посте ).

Код, который конфигурирует пример данных

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/** Simple U.S. state representation. */
   private enum State
   {
      ALASKA("Alaska"),
      CALIFORNIA("California"),
      COLORADO("Colorado"),
      NEW_YORK("New York"),
      RHODE_ISLAND("Rhode Island"),
      TEXAS("Texas"),
      WYOMING("Wyoming");
 
      private String stateName;
 
      State(final String newStateName)
      {
         this.stateName = newStateName;
      }
   }
 
   /** Simple Movie representation. */
   private enum Movie
   {
      STAR_WARS("Star Wars"),
      EMPIRE_STRIKES_BACK("The Empire Strikes Back"),
      RAIDERS_OF_THE_LOST_ARK("Raiders of the Lost Ark"),
      INCEPTION("Inception"),
      CHRISTMAS_VACATION("Christmas Vacation"),
      CHRISTMAS_VACATION_2("Christmas Vacation 2"),
      FLETCH("Fletch");
 
      private String movieName;
 
      Movie(final String newMovieName)
      {
         this.movieName = newMovieName;
      }
   }
 
   /** Mapping of state name to area of state measured in kilometers. */
   private final static Map<String, Long> statesLandSizeKm;
 
   /** Mapping of state name to estimated number of people living in that state. */
   private final static Map<String, Long> statesPopulation;
 
   /** Normal audience movie ratings on Rotten Tomatoes. */
   private final static Map<String, Double> movieRatingsNormal;
 
   /** Critics movie ratings on Rotten Tomatoes. */
   private final static Map<String, Double> movieRatingsCritics;
 
   /** Dustin's movie ratings. */
   private final static Map<String, Double> movieRatingsDustin;
 
   /** Maximum population to be shown on bar charts of states' populations.  */
   private final static long POPULATION_RANGE_MAXIMUM = 40_000_000L;
 
   /** Maximum land area (km) to be shown on bar charts of states' land areas. */
   private final static long LAND_AREA_KM_MAXIMUM = 1_800_000L;
 
   /** Maximum movie rating to be shown on bar charts. */
   private final static double MOVIE_RATING_MAXIMUM = 10.0;
 
   /** Width of chart. */
   private final static int CHART_WIDTH = 750;
 
   /** Height of chart. */
   private final static int CHART_HEIGHT = 600;
 
   /** Width of chart for Movie Ratings. */
   private final static int MOVIE_CHART_WIDTH = CHART_WIDTH + 350;
 
   /* Initialize final static variables. */
   static
   {
      statesLandSizeKm =
         ImmutableMap.<String, Long>builder()
                     .put(State.ALASKA.stateName, 1_717_854L)
                     .put(State.CALIFORNIA.stateName, 423_970L)
                     .put(State.COLORADO.stateName, 269_601L)
                     .put(State.NEW_YORK.stateName, 141_299L)
                     .put(State.RHODE_ISLAND.stateName, 4_002L)
                     .put(State.TEXAS.stateName, 695_621L)
                     .put(State.WYOMING.stateName, 253_336L)
                     .build();
 
      statesPopulation =
         ImmutableMap.<String, Long>builder()
                     .put(State.ALASKA.stateName, 722_718L)
                     .put(State.CALIFORNIA.stateName, 37_691_912L)
                     .put(State.COLORADO.stateName, 5_116_769L)
                     .put(State.NEW_YORK.stateName, 19_465_197L)
                     .put(State.RHODE_ISLAND.stateName, 1_051_302L)
                     .put(State.TEXAS.stateName, 25_674_681L)
                     .put(State.WYOMING.stateName, 568_158L)
                     .build();
 
      movieRatingsNormal =
         ImmutableMap.<String, Double>builder()
                     .put(Movie.CHRISTMAS_VACATION.movieName, 8.3)
                     .put(Movie.CHRISTMAS_VACATION_2.movieName, 1.3)
                     .put(Movie.STAR_WARS.movieName, 9.3)
                     .put(Movie.EMPIRE_STRIKES_BACK.movieName, 9.4)
                     .put(Movie.RAIDERS_OF_THE_LOST_ARK.movieName, 9.3)
                     .put(Movie.INCEPTION.movieName, 9.3)
                     .put(Movie.FLETCH.movieName, 7.8)
                     .build();
 
      movieRatingsCritics =
         ImmutableMap.<String, Double>builder()
                     .put(Movie.CHRISTMAS_VACATION.movieName, 6.3)
                     .put(Movie.CHRISTMAS_VACATION_2.movieName, 0.0)
                     .put(Movie.STAR_WARS.movieName, 9.4)
                     .put(Movie.EMPIRE_STRIKES_BACK.movieName, 9.7)
                     .put(Movie.RAIDERS_OF_THE_LOST_ARK.movieName, 9.4)
                     .put(Movie.INCEPTION.movieName, 8.6)
                     .put(Movie.FLETCH.movieName, 7.5)
                     .build();
      movieRatingsDustin =
         ImmutableMap.<String, Double>builder()
                     .put(Movie.CHRISTMAS_VACATION.movieName, 7.0)
                     .put(Movie.CHRISTMAS_VACATION_2.movieName, 0.0)
                     .put(Movie.STAR_WARS.movieName, 9.5)
                     .put(Movie.EMPIRE_STRIKES_BACK.movieName, 10.0)
                     .put(Movie.RAIDERS_OF_THE_LOST_ARK.movieName, 10.0)
                     .put(Movie.INCEPTION.movieName, 9.0)
                     .put(Movie.FLETCH.movieName, 9.0)
                     .build();                    
   }

Следующий листинг кода демонстрирует загрузку примера приложения. Это включает однострочную основную функцию, которая обычно запускает Java-приложения, и более интересный метод start (String []), который переопределяется из расширенного класса Application . В этом листинге кода также используется возможность переключения строк в Java 7 для быстрой и простой реализации анализа аргументов командной строки для запуска конкретной демонстрации генерации диаграммы. В этом примере демонстрируется, что аргументы командной строки, передаваемые в Application.launch (String…) , доступны приложению JavaFX через метод Application.getParameters (), который возвращает экземпляр вложенного Application.Parameters .

Код, который запускает демонстрационный пример JavaFX 2.0 Charting

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
    * Start JavaFX application.
    *
    * @param stage First stage of JavaFX application.
    * @throws Exception
    */
   @Override
   public void start(final Stage stage) throws Exception
   {
      final Parameters parameters = getParameters();  // command-line args
      final List<String> args = parameters.getUnnamed();
      final String firstArgument = !args.isEmpty() ? args.get(0) : "1";
      final int chartWidth = !firstArgument.equals("4") ? CHART_WIDTH : MOVIE_CHART_WIDTH;
 
      stage.setTitle("Building Bar Charts");
      final Group rootGroup = new Group();
      final Scene scene = new Scene(rootGroup, chartWidth, CHART_HEIGHT, Color.WHITE);
      stage.setScene(scene);
 
      switch (firstArgument)
      {
         case "1" :
            rootGroup.getChildren().add(buildVerticalLandAreaBarChart());
            break;
         case "2" :
            rootGroup.getChildren().add(buildVerticalPopulationBarChart());
            break;
         case "3" :
            rootGroup.getChildren().add(buildHorizontalPopulationBarChart());
            break;
         case "4" :
            rootGroup.getChildren().add(buildVerticalMovieRatingsBarChart());
            break;
         case "5" :
            rootGroup.getChildren().add(buildStatesLandSizePopulationScatterChart());
            break;
         default :
            rootGroup.getChildren().add(buildVerticalLandAreaBarChart());
      }
      stage.show();
   }
 
   /**
    * Main function for demonstrating JavaFX 2.0 bar chart and scatter chart.
    *
    * @param arguments Command-line arguments: none expected.
    */
   public static void main(final String[] arguments)
   {
      Application.launch(arguments);
   }

После того, как данные для заполнения диаграмм будут настроены, а базовая привязка к загрузке приложения JavaFX продемонстрирована, пришло время взглянуть на использование реальных API-интерфейсов диаграмм JavaFX 2.0. Как показывает приведенный выше код, первый вариант («1») приводит к генерации вертикальной гистограммы, изображающей относительные площади суши состояний выборки в километрах. Методы, выполненные для этого примера, показаны далее.

Создание вертикальной гистограммы с земельными участками штатов

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
    * Build ObservableList of XYChart.Series instances mapping state names to
    * land areas.
    *
    * @return ObservableList of XYChart.Series instances mapping state names to
    *    land areas.
    */
   public ObservableList<XYChart.Series<String,Long>> buildStatesToLandArea()
   {
      final ObservableList<XYChart.Data<String,Long>> statesToLandArea =
         FXCollections.observableArrayList();
      for (final State state : State.values())
      {
         final XYChart.Data<String,Long> stateAreaData =
            new XYChart.Data<String,Long>(
               state.stateName, statesLandSizeKm.get(state.stateName));
         statesToLandArea.add(stateAreaData);
      }
      final XYChart.Series<String, Long> landSeries =
         new XYChart.Series<String, Long>(statesToLandArea);
      final ObservableList<XYChart.Series<String, Long>> series =
         FXCollections.observableArrayList();
      landSeries.setName("State Land Size (km)");
      series.add(landSeries);
      return series;
   }
 
   /**
    * Provides a CategoryAxis instantiated with sample states' names.
    *
    * @return CategoryAxis with sample states' names.
    */
   public CategoryAxis buildStatesNamesCategoriesAxis()
   {
      final ObservableList<String> stateNames = FXCollections.observableArrayList();
      stateNames.addAll(
         State.ALASKA.stateName,
         State.CALIFORNIA.stateName,
         State.COLORADO.stateName,
         State.NEW_YORK.stateName,
         State.RHODE_ISLAND.stateName,
         State.TEXAS.stateName,
         State.WYOMING.stateName);
      final CategoryAxis categoryAxis = new CategoryAxis(stateNames);
      categoryAxis.setLabel("State");
      categoryAxis.setMinWidth(CHART_WIDTH);
      return categoryAxis;
   }
 
   /**
    * Build vertical bar chart comparing land areas of sample states.
    *
    * @return Vertical bar chart comparing land areas of sample states.
    */
   public XYChart buildVerticalLandAreaBarChart()
   {
      final ValueAxis landAreaAxis = new NumberAxis(0, LAND_AREA_KM_MAXIMUM, 50_000);
      final BarChart landAreaBarChart =
         new BarChart(buildStatesNamesCategoriesAxis(), landAreaAxis, buildStatesToLandArea());
      landAreaBarChart.setMinWidth(CHART_WIDTH);
      landAreaBarChart.setMinHeight(CHART_HEIGHT);
      landAreaBarChart.setTitle("Land Area (in km) of Select U.S. States");
      return landAreaBarChart;
   }

Приведенный выше фрагмент кода показывает три метода, которые я использовал для создания гистограммы. Метод внизу buildVerticalLandAreaBarChart () создает экземпляр NumberAxis для оси Y диаграммы и использует эту реализацию ValueAxis при создании экземпляра BarChart . Создание экземпляра BarChart вызывает два других метода во фрагменте кода для создания оси X с именами состояний и для подготовки данных в формате ObservableList <XYChart.Series <String, Long >> для использования при генерации диаграммы. Сгенерированный график показан далее.

Подобный код может привести к аналогичной диаграмме для изображения популяций состояний выборки. Код для этого показан ниже и сопровождается снимком экрана сгенерированного графика.

Создание вертикальной гистограммы с населением штатов

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// method buildStatesNamesCategoriesAxis() was shown in previous code listing
 
   /**
    * Build one or more series of XYChart Data representing state names as 'x'
    * portion and state populations as 'y' portion. This method is likely to be
    * used in vertical presentations where state names are desired on the x-axis
    * and population numbers are desired on the y-axis.
    *
    * @return Series of XYChar Data representing state names as 'x' portion and
    *    state populations as 'y' portion.
    */
   public ObservableList<XYChart.Series<String,Long>> buildStatesToPopulation()
   {
      final ObservableList<XYChart.Data<String,Long>> statesToPopulation =
         FXCollections.observableArrayList();
      for (final State state : State.values())
      {
         final XYChart.Data<String,Long> statePopulationData =
            new XYChart.Data<String,Long>(
               state.stateName, statesPopulation.get(state.stateName));
         statesToPopulation.add(statePopulationData);
      }
      final XYChart.Series<String, Long> populationSeries =
         new XYChart.Series<String, Long>(statesToPopulation);
      final ObservableList<XYChart.Series<String, Long>> series =
         FXCollections.observableArrayList();
      populationSeries.setName("State Population");
      series.add(populationSeries);
      return series;
   }
 
   /**
    * Build vertical bar chart comparing populations of sample states.
    *
    * @return Vertical bar chart comparing populations of sample states.
    */
   public XYChart buildVerticalPopulationBarChart()
   {
      final ValueAxis populationAxis = new NumberAxis(0, POPULATION_RANGE_MAXIMUM, 2_000_000);
      final BarChart populationBarChart =
         new BarChart(buildStatesNamesCategoriesAxis(), populationAxis, buildStatesToPopulation());
      populationBarChart.setMinWidth(CHART_WIDTH);
      populationBarChart.setMinHeight(CHART_HEIGHT);
      populationBarChart.setTitle("Population of Select U.S. States");
      return populationBarChart;
   }

Предыдущие две диаграммы были вертикальными диаграммами. В следующем примере для данных выборки используется та же совокупность состояний, что и в последнем примере, но она представлена ​​горизонтальной гистограммой, а не вертикальной диаграммой. Обратите внимание, что для генерации оси с именами состояний используется тот же метод, что и в двух последних примерах, но его результат передается в качестве второго аргумента конструктору BarChart, а не в качестве первого аргумента. Это изменение порядка в конструкторе BarChart меняет диаграмму с вертикальной на горизонтальную. Другими словами, наличие CategoryAxis в качестве первого аргумента и ValueAxis в качестве второго аргумента для конструктора BarChart приводит к вертикальной диаграмме. Переключение порядка этих двух типов Оси приводит к горизонтальной диаграмме. Я также должен был изменить порядок отображения данных, чтобы ключевая часть была населением, а часть значения — именами состояний. Код сопровождается выводом.

Создание горизонтальной гистограммы с населением штатов

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// method buildStatesNamesCategoriesAxis() was shown in previous code listings
 
   /**
    * Build one or more series of XYChart Data representing population as 'x'
    * portion and state names as 'y' portion. This method is likely to be used
    * in horizontal presentations where state names are desired on the y-axis
    * and population numbers are desired on the x-axis.
    *
    * @return Series of XYChar Data representing population as 'x' portion and
    *    state names as 'y' portion.
    */
   public ObservableList<XYChart.Series<Long,String>> buildPopulationToStates()
   {
      final ObservableList<XYChart.Data<Long,String>> statesToPopulation =
         FXCollections.observableArrayList();
      for (final State state : State.values())
      {
         final XYChart.Data<Long,String> statePopulationData =
            new XYChart.Data<Long,String>(
               statesPopulation.get(state.stateName), state.stateName);
         statesToPopulation.add(statePopulationData);
      }
      final XYChart.Series<Long, String> populationSeries =
         new XYChart.Series<Long, String>(statesToPopulation);
      final ObservableList<XYChart.Series<Long, String>> series =
         FXCollections.observableArrayList();
      populationSeries.setName("State Population");
      series.add(populationSeries);
      return series;
   }
 
   /**
    * Build horizontal bar chart comparing populations of sample states.
    *
    * @return Horizontal bar chart comparing populations of sample states.
    */
   public XYChart buildHorizontalPopulationBarChart()
   {
      final ValueAxis populationAxis = new NumberAxis(0, POPULATION_RANGE_MAXIMUM, 2_000_000);
      final BarChart populationBarChart =
         new BarChart(populationAxis, buildStatesNamesCategoriesAxis(), buildPopulationToStates());
      populationBarChart.setMinWidth(CHART_WIDTH);
      populationBarChart.setTitle("Population of Select U.S. States");
      return populationBarChart;
   }

Для всех этих примеров создания гистограммы я использовал XYChart JavaFX. Оказывается, ScatterChart также расширяет XYChart , поэтому его использование аналогично использованию BarChart . Большая разница в этом случае ( ScatterChart ) состоит в том, что будут существовать две ориентированные на значения оси. Другими словами, вместо использования имен состояний для оси x (по вертикали) или для оси y (по горизонтали), каждая ось будет основываться на значениях (площадь суши для оси x и население для оси y). Эти типы диаграмм часто используются для визуального определения степени корреляции между данными. Код для генерации этого и вывод, который он генерирует, показан далее.

Составление точечной диаграммы населения штата к размеру государственной земли

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
    * Build mapping of land area to population for each state.
    *
    * @return Mapping of land area to population for each sample state.
    */
   public ObservableList<XYChart.Series<Long,Long>> buildAreaToPopulation()
   {
      final ObservableList<XYChart.Data<Long,Long>> areaToPopulation =
         FXCollections.observableArrayList();
      for (final State state : State.values())
      {
         final XYChart.Data<Long,Long> areaPopulationData =
            new XYChart.Data<Long,Long>(
               statesLandSizeKm.get(state.stateName),
               statesPopulation.get(state.stateName));
         areaToPopulation.add(areaPopulationData);
      }
      final XYChart.Series<Long, Long> areaPopulationSeries =
         new XYChart.Series<Long, Long>(areaToPopulation);
      final ObservableList<XYChart.Series<Long, Long>> series =
         FXCollections.observableArrayList();
      areaPopulationSeries.setName("State Land Area and Population");
      series.add(areaPopulationSeries);
      return series;
   }
 
 
   /**
    * Build a Scatter Chart depicting correlation between land area and population
    * for each state.
    *
    * @return Scatter Chart depicting correlation between land area and population
    *    for each state.
    */
   public XYChart buildStatesLandSizePopulationScatterChart()
   {
      final ValueAxis xAxis = new NumberAxis(0, LAND_AREA_KM_MAXIMUM, 50_000);
      xAxis.setLabel("Land Area (km)");
      final ValueAxis yAxis = new NumberAxis(0, POPULATION_RANGE_MAXIMUM, 2_000_000);
      yAxis.setLabel("Population");
      final ScatterChart xyChart = new ScatterChart(xAxis, yAxis, buildAreaToPopulation());
      xyChart.setMinHeight(CHART_HEIGHT);
 
      return xyChart;
   }

Точечные диаграммы помогают визуально определить, есть ли какая-либо корреляция между размером земли штата и его населением. Частично потому, что Аляска и Вайоминг включены в набор состояний выборки, корреляции не так много. Существует еще многое, что можно сделать, чтобы стилизовать диаграмму рассеяния JavaFX .

Иногда полезно увидеть более одной серии, нанесенной на одну гистограмму. Чтобы проиллюстрировать несколько рядов на одной гистограмме, я собираюсь изменить одни и те же данные. Вместо того, чтобы использовать данные о состояниях, их размерах и населении, я собираюсь использовать данные, показанные в исходном списке кода, связанные с рейтингами фильмов. В частности, здесь есть три серии: рейтинги критиков, рейтинги «нормальной» аудитории и мои собственные рейтинги. Как и в предыдущих примерах, сначала я показываю код (наиболее интересная часть — метод buildRatingsToMovieTitle ()) , а затем вывод.

Создание гистограммы рейтинга фильма с несколькими сериями (несколько рейтинговых групп)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
    * Build one or more series of XYChart Data representing movie names as 'x'
    * portion and movie ratings as 'y' portion. This method is likely to be
    * used in vertical presentations where movie names are desired on the x-axis
    * and movie ratings are desired on the y-axis. This method illustrates
    * multiple series as ratings for both normal audience members and critics
    * are shown.
    *
    * @return Series of XYChar Data representing state movie names as 'x' portion
    *    and movie ratings as 'y' portion.
    */
   public ObservableList<XYChart.Series<String,Double>> buildRatingsToMovieTitle()
   {
      final ObservableList<XYChart.Data<String,Double>> normalRatings =
         FXCollections.observableArrayList();
      final ObservableList<XYChart.Data<String,Double>> criticRatings =
         FXCollections.observableArrayList();
      final ObservableList<XYChart.Data<String,Double>> dustinRatings =
         FXCollections.observableArrayList();
      for (final Movie movie : Movie.values())
      {
         final XYChart.Data<String,Double> normalRatingsData =
            new XYChart.Data<String,Double>(
               movie.movieName, movieRatingsNormal.get(movie.movieName));
         normalRatings.add(normalRatingsData);
         final XYChart.Data<String,Double> criticRatingsData =
            new XYChart.Data<String,Double>(
               movie.movieName, movieRatingsCritics.get(movie.movieName));
         criticRatings.add(criticRatingsData);
         final XYChart.Data<String,Double> dustinRatingsData =
            new XYChart.Data<String,Double>(
               movie.movieName, movieRatingsDustin.get(movie.movieName));
         dustinRatings.add(dustinRatingsData);
      }
      final XYChart.Series<String, Double> normalSeries =
         new XYChart.Series<String, Double>(normalRatings);
      normalSeries.setName("Normal Audience");
      final XYChart.Series<String, Double> criticSeries =
         new XYChart.Series<String, Double>(criticRatings);
      criticSeries.setName("Critics");
      final XYChart.Series<String, Double> dustinSeries =
         new XYChart.Series<String, Double>(dustinRatings);
      dustinSeries.setName("Dustin");
      final ObservableList<XYChart.Series<String, Double>> series =
         FXCollections.observableArrayList();
      series.add(normalSeries);
      series.add(criticSeries);
      series.add(dustinSeries);
      return series;
   }
 
   /**
    * Build vertical bar chart comparing movie ratings to demonstrate multiple
    * series used in a single chart.
    *
    * @return Vertical bar chart comparing movie ratings.
    */
   public XYChart buildVerticalMovieRatingsBarChart()
   {
      final ValueAxis ratingAxis = new NumberAxis(0, MOVIE_RATING_MAXIMUM, 1.0);
      final BarChart ratingBarChart =
         new BarChart(buildMovieRatingsAxis(), ratingAxis, buildRatingsToMovieTitle());
      ratingBarChart.setMinWidth(MOVIE_CHART_WIDTH);
      ratingBarChart.setMinHeight(CHART_HEIGHT);
      ratingBarChart.setTitle("Movie Ratings");
      return ratingBarChart;
   }

Бета-версия JavaFX 2.1 включает в себя несколько новых диаграмм, включая StackedBarChart . Гистограмма с накоплением подразумевает наличие нескольких рядов, поэтому я адаптирую последний пример для использования одного из них. Столбчатая диаграмма с накоплением покажет каждый из трех источников оценок, вносящих вклад в один столбец на фильм, а не на три столбца на фильм, как в последнем примере.

Создание StackedBarChart рейтингов фильмов

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
    * Build one or more series of XYChart Data representing movie names as 'x'
    * portion and movie ratings as 'y' portion. This method is likely to be
    * used in vertical presentations where movie names are desired on the x-axis
    * and movie ratings are desired on the y-axis. This method illustrates
    * multiple series as ratings for both normal audience members and critics
    * are shown.
    *
    * @return Series of XYChar Data representing state movie names as 'x' portion
    *    and movie ratings as 'y' portion.
    */
   public ObservableList<XYChart.Series<String,Double>> buildRatingsToMovieTitle()
   {
      final ObservableList<XYChart.Data<String,Double>> normalRatings =
         FXCollections.observableArrayList();
      final ObservableList<XYChart.Data<String,Double>> criticRatings =
         FXCollections.observableArrayList();
      final ObservableList<XYChart.Data<String,Double>> dustinRatings =
         FXCollections.observableArrayList();
      for (final Movie movie : Movie.values())
      {
         final XYChart.Data<String,Double> normalRatingsData =
            new XYChart.Data<String,Double>(
               movie.movieName, movieRatingsNormal.get(movie.movieName));
         normalRatings.add(normalRatingsData);
         final XYChart.Data<String,Double> criticRatingsData =
            new XYChart.Data<String,Double>(
               movie.movieName, movieRatingsCritics.get(movie.movieName));
         criticRatings.add(criticRatingsData);
         final XYChart.Data<String,Double> dustinRatingsData =
            new XYChart.Data<String,Double>(
               movie.movieName, movieRatingsDustin.get(movie.movieName));
         dustinRatings.add(dustinRatingsData);
      }
      final XYChart.Series<String, Double> normalSeries =
         new XYChart.Series<String, Double>(normalRatings);
      normalSeries.setName("Normal Audience");
      final XYChart.Series<String, Double> criticSeries =
         new XYChart.Series<String, Double>(criticRatings);
      criticSeries.setName("Critics");
      final XYChart.Series<String, Double> dustinSeries =
         new XYChart.Series<String, Double>(dustinRatings);
      dustinSeries.setName("Dustin");
      final ObservableList<XYChart.Series<String, Double>> series =
         FXCollections.observableArrayList();
      series.add(normalSeries);
      series.add(criticSeries);
      series.add(dustinSeries);
      return series;
   }
 
 
   /**
    * Build a Stacked Bar Chart depicting total ratings of each movie based on
    * contributions of three ratings groups.
    *
    * @return Stacked Bar Chart depicting three rating groups' contributions
    *    to overall movie rating.
    */
   public XYChart buildStackedMovieRatingsBarChart()
   {
      final ValueAxis ratingAxis = new NumberAxis(0, MOVIE_RATING_MAXIMUM*3, 2.5);
      final StackedBarChart ratingBarChart =
         new StackedBarChart(buildMovieRatingsAxis(), ratingAxis, buildRatingsToMovieTitle());
      ratingBarChart.setMinWidth(MOVIE_CHART_WIDTH);
      ratingBarChart.setMinHeight(CHART_HEIGHT);
      ratingBarChart.setTitle("Movie Ratings");
      return ratingBarChart;
   }

Сложенная гистограмма полезна, потому что она обеспечивает быстрый просмотр общего составного рейтинга каждого фильма вместе с тем, как каждая группа рецензентов внесла свой вклад в этот общий рейтинг.

Документация по диаграммам JavaFX 2.0
Учебное пособие « Использование диаграмм JavaFX» охватывает примеры кода и соответствующие сгенерированные изображения диаграмм для различных типов диаграмм JavaFX 2.0, таких как круговая диаграмма , линейная диаграмма , диаграмма с областями , пузырьковая диаграмма , точечная диаграмма и гистограмма . В этом руководстве также представлены разделы по использованию CSS для стилизации диаграмм и информации по подготовке данных диаграммы и созданию пользовательских диаграмм.

Вывод
В этом посте продемонстрировано использование пакета диаграмм JavaFX для создания гистограмм, точечных диаграмм и диаграмм с накоплением. Когда JavaFX принят как стандартная часть Java SE SDK, он привнесет в SDK стандартный механизм генерации диаграмм в Java.

Справка: линейчатые и точечные диаграммы JavaFX 2.0 (и StackedBarCharts JavaFX 2.1) от нашего партнера по JCG