JavaFX 2.0条形图和散点图(以及JavaFX 2.1 StackedBarCharts)
在演示JavaFX 2.0图表API之前,第一个代码清单显示了示例中要使用的数据的配置。 在更现实的情况下,我将从数据存储中获取此数据,但是在这种情况下,我只是直接将其直接包含在源代码中,以方便示例访问。 尽管此代码本身与JavaFX 2.0图表并没有内在的联系,但还是有一些有趣的事情。 该代码在数字文字中使用了Java 7的下划线,从而使读取数据中使用的样本状态的地域大小和总体更加容易。 该代码清单还使用了Guava的ImmutableMap类(在上一篇文章中介绍了 )。
配置示例数据的代码
/** 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应用程序的单行主函数,以及从扩展Application类中重写的更有趣的start(String [])方法。 此代码清单还利用Java 7的Strings-on-Strings功能对命令行参数解析进行了快速而又肮脏的实现,以运行特定的图表生成演示。 该示例演示了传递给Application.launch(String ...)的命令行参数可通过Application.getParameters()方法返回给嵌套的Application.Parameters实例的JavaFX应用程序使用。
启动JavaFX 2.0图表演示示例的代码
/** * 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应用程序引导,现在该开始研究实际JavaFX 2.0图表API的使用了。 如上面的代码所示,第一个选项(“ 1”)导致生成垂直条形图,该条形图描述了样本州的相对陆地面积(以千米为单位)。 接下来显示该示例执行的方法。
生成州土地面积的垂直条形图
/** * 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实例化调用代码片段中的其他两个方法来创建带有状态名称的x轴,并准备ObservableList <XYChart.Series <String,Long >>格式的数据以用于图表生成。 生成的图表如下所示。
相似的代码可以生成用于描述样本状态总体的相似图表。 接下来显示执行此操作的代码,然后是生成的图表的屏幕快照。
使用州人口生成垂直条形图
// 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构造函数的顺序更改将图表从垂直更改为水平。 换句话说,在BarChart构造函数中,将CategoryAxis作为第一个参数,将ValueAxis作为第二个参数将导致一个垂直图表。 切换这两种类型的Axis的顺序会产生一个水平图。 我还必须切换要绘制的数据的映射顺序,以便关键部分是填充,而值部分是状态名称。 该代码后跟输出。
用州人口生成水平条形图
// 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; }
对于生成条形图的所有这些示例,我都使用了JavaFX的XYChart 。 事实证明ScatterChart也扩展了XYChart ,因此其用法与BarChart相似。 这种情况( ScatterChart )的最大区别是将存在两个面向值的轴。 换句话说,每个轴将基于值(x轴的土地面积和y轴的人口数),而不是使用x轴(垂直)或y轴(水平)的州名。 这些类型的图表通常用于直观地确定数据之间的相关程度。 接下来显示生成此代码的代码及其生成的输出。
生成州人口到州土地面积散布图
/** * 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()方法) ,然后是输出。
生成具有多个系列的电影分级条形图(多个分级组)
/** * 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 beta版包含几个新图表,包括StackedBarChart 。 堆叠的条形图表示多个序列,因此我将调整最后一个示例以使用其中的一个。 堆叠的条形图将显示三个评级源中的每一个,每个电影贡献一个条形,而不像上一个示例那样每个电影三个条形。
生成电影分级的StackedBarChart
/** * 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图表的标准机制。
参考:来自JCG合作伙伴的 JavaFX 2.0条形图和散点图(以及JavaFX 2.1 StackedBarCharts) 实际事件启发博客中的达斯汀·马克思。
翻译自: https://www.javacodegeeks.com/2012/02/javafx-20-bar-and-scatter-charts-and.html