java 和javafx_JavaFX 2 XYCharts和Java 7功能

java 和javafx

我最喜欢的JavaFX 2功能之一是它在javafx.scene.chart包中提供的标准图表。 该软件包提供了几种不同类型的现成图表。 除其中之一( PieChart )外,所有其他均为“ 2轴图”( XYChart的特定实现)。 在本文中,我研究了XYChart这些专业之间的共性。 在此过程中,我将介绍一些方便的Java 7功能。

接下来显示javafx.scene.chart包中关键图类型的UML类图。 注意AreaChartStackedAreaChartBarChartStackedBarChartBubbleChartLineChartScatterChart都扩展了XYChart

正如上面的UML图(使用JDeveloper生成)所示, PieChart直接扩展Chart ,而所有其他图表类型都扩展XYChart 。 因为除PieChart之外的所有其他图表类型都扩展了XYChart ,所以它们共享一些共同的功能。 例如,它们都是带有水平('x')轴和垂直('y')轴的2轴图表。 它们通常允许为所有XY图表以相同的格式(数据结构)指定数据。 这篇文章的其余部分演示了能够对大多数XYChart使用相同的数据。

图表的主要用途是显示数据,因此下一个代码清单指示从Oracle数据库中的“ hr样本模式检索数据。 请注意,JDBC_URL,USERNAME,PASSWORD和AVG_SALARIES_PER_DEPARTMENT_QUERY是在JDBC连接和查询中使用的常量字符串。

getAverageDepartmentsSalaries()

/**
 * Provide average salary per department name.
 * 
 * @return Map of department names to average salary per department.
 */
public Map<String, Double> getAverageDepartmentsSalaries()
{
   final Map<String, Double> averageSalaryPerDepartment = new HashMap<>();
   try (final Connection connection = DriverManager.getConnection(JDBC_URL, USERNAME, PASSWORD);
        final Statement statement = connection.createStatement();
        final ResultSet rs = statement.executeQuery(AVG_SALARIES_PER_DEPARTMENT_QUERY))
   {
      while (rs.next())
      {
         final String departmentName = rs.getString(COLUMN_DEPARTMENT_NAME);
         final Double salaryAverage = rs.getDouble(ALIAS_AVERAGE_SALARY);
         averageSalaryPerDepartment.put(departmentName, salaryAverage);
      }
   }
   catch (SQLException sqlEx)
   {
      LOGGER.log(
         Level.SEVERE,
         'Unable to get average salaries per department - {0}', sqlEx.toString());
   }
   return averageSalaryPerDepartment;
}

上面的Java代码段使用JDBC检索数据,以将部门名称字符串Map为每个部门中雇员的平均工资。 此代码中使用了几个方便的Java 7功能。 一个小的功能是,与局部变量averageSalaryPerDepartment (第8行)的声明一起使用的钻石运算符的推断通用参数化类型。 这是语法糖的一小部分,但确实使代码更简洁。

Java 7的一项更重要的功能是使用try-with-resources语句来处理ConnectionStatementResultSet资源(第9-11行)。 与以前使用JDBC相比,即使面对异常,这也是处理这些资源打开和关闭的一种更好的方法。 try-with-resources语句上的Java Tutorials页面广告该语句“确保在语句末尾关闭每个资源”,并且无论“ try语句正常完成还是突然完成”,每个资源都将“关闭”。 该页面还指出,与上述代码一样,在同一语句中指定了多个资源时,“资源的close方法将按其创建的相反顺序进行调用。”

从数据库中检索到的数据可以放入适当的数据结构中,以支持大多数XYCharts的使用。 这在下一个方法中显示。

ChartMaker.createXyChartDataForAverageDepartmentSalary(地图)

/**
 * Create XYChart Data representing average salary per department name.
 * 
 * @param newAverageSalariesPerDepartment Map of department name (keys) to
 *    average salary for each department (values).
 * @return XYChart Data representing average salary per department.
 */
public static ObservableList<XYChart.Series<String, Double>> createXyChartDataForAverageDepartmentSalary(
   final Map<String, Double> newAverageSalariesPerDepartment)
{
   final Series<String, Double> series = new Series<>();
   series.setName('Departments');
   for (final Map.Entry<String, Double> entry : newAverageSalariesPerDepartment.entrySet())
   {
      series.getData().add(new XYChart.Data<>(entry.getKey(), entry.getValue()));
   }
   final ObservableList<XYChart.Series<String, Double>> chartData =
      FXCollections.observableArrayList();

   chartData.add(series);
   return chartData;
}

刚刚显示的方法将检索到的数据放置在几乎所有基于XYChart的图表都可以使用的数据结构中。 现在,将检索到的数据打包到JavaFX可观察的集合中,就可以轻松生成图表。 下一个代码片段显示了用于生成多个基于XYChart的图表(面积,条形图,气泡图,折线图和散点图)的方法。 请注意它们都是多么相似,以及如何使用相同方法提供的相同数据。 StackedBar和StackedArea图表也可以使用类似的数据,但此处未显示,因为它们对于本示例中使用的单个数据系列没有意义。

生成除气泡图和堆积图以外的XY图的方法

private XYChart<String, Double> generateAreaChart(
   final Axis<String> xAxis, final Axis<Double> yAxis)
{
   final AreaChart<String, Double> areaChart =
      new AreaChart<>(
         xAxis, yAxis,
         ChartMaker.createXyChartDataForAverageDepartmentSalary(
            this.databaseAccess.getAverageDepartmentsSalaries()));
   return areaChart;
}

private XYChart<String, Double> generateBarChart(
   final Axis<String> xAxis, final Axis<Double> yAxis)
{
   final BarChart<String, Double> barChart =
      new BarChart<>(
         xAxis, yAxis,
         ChartMaker.createXyChartDataForAverageDepartmentSalary(
            this.databaseAccess.getAverageDepartmentsSalaries()));
   return barChart;
}

private XYChart<Number, Number> generateBubbleChart(
   final Axis<String> xAxis, final Axis<Double> yAxis)
{
   final Axis<Number> deptIdXAxis = new NumberAxis();
   deptIdXAxis.setLabel('Department ID');
   final BubbleChart<Number, Number> bubbleChart =
      new BubbleChart(
         deptIdXAxis, yAxis,
         ChartMaker.createXyChartDataForAverageDepartmentSalaryById(
            this.databaseAccess.getAverageDepartmentsSalariesById()));
   return bubbleChart;
}

private XYChart<String, Double> generateLineChart(
        final Axis<String> xAxis, final Axis<Double> yAxis)
{
   final LineChart<String, Double> lineChart =
      new LineChart<>(
         xAxis, yAxis,
         ChartMaker.createXyChartDataForAverageDepartmentSalary(
            this.databaseAccess.getAverageDepartmentsSalaries()));
   return lineChart;
}

private XYChart<String, Double> generateScatterChart(
   final Axis<String> xAxis, final Axis<Double> yAxis)
{
   final ScatterChart<String, Double> scatterChart =
      new ScatterChart<>(
         xAxis, yAxis,
         ChartMaker.createXyChartDataForAverageDepartmentSalary(
            this.databaseAccess.getAverageDepartmentsSalaries()));
   return scatterChart;
}

这些方法是如此相似,以至于我实际上可以使用方法句柄(或更传统的反射API)来反射性地调用适当的图表构造函数,而不是使用单独的方法。 但是,我在2月的2013年RMOUG培训日演讲中使用了这些功能,因此想保留图表特定的构造函数,以使它们对观众更清晰。

XYChart类型的常规处理的一个例外是BubbleChart的处理。 此图表的x轴需要数字类型,因此上面提供的基于字符串(部门名称)的x轴数据将不起作用。 另一种方法(此处未显示)提供了一个查询,该查询按部门ID(长)而不是部门名称返回平均工资。 接下来显示稍有不同的generateBubbleChart方法。

generateBubbleChart(Axis,Axis)

private XYChart<Number, Number> generateBubbleChart(
      final Axis<String> xAxis, final Axis<Double> yAxis)
   {
      final Axis<Number> deptIdXAxis = new NumberAxis();
      deptIdXAxis.setLabel('Department ID');
      final BubbleChart<Number, Number> bubbleChart =
         new BubbleChart(
            deptIdXAxis, yAxis,
            ChartMaker.createXyChartDataForAverageDepartmentSalaryById(
               this.databaseAccess.getAverageDepartmentsSalariesById()));
      return bubbleChart;
   }

可以编写代码直接调用这些不同的图表生成方法,但这为使用Java 7的方法句柄提供了一个很好的机会。 下一个代码片段显示了此操作。 该代码不仅演示了方法句柄,而且还使用了Java 7的多捕获异常处理机制(第77行)。

/**
 * Generate JavaFX XYChart-based chart.
 * 
 * @param chartChoice Choice of chart to be generated.
 * @return JavaFX XYChart-based chart; may be null.
 * @throws IllegalArgumentException Thrown if the provided parameter is null.
 */
private XYChart<String, Double> generateChart(final ChartTypes chartChoice)
{
   XYChart<String, Double> chart = null;
   final Axis<String> xAxis = new CategoryAxis();
   xAxis.setLabel('Department Name');
   final Axis<? extends Number> yAxis = new NumberAxis();
   yAxis.setLabel('Average Salary');
   if (chartChoice == null)
   {
      throw new IllegalArgumentException(
         'Provided chart type was null; chart type must be specified.');
   }
   else if (!chartChoice.isXyChart())
   {
      LOGGER.log(
         Level.INFO,
         'Chart Choice {0} {1} an XYChart.',
         new Object[]{chartChoice.name(), chartChoice.isXyChart() ? 'IS' : 'is NOT'});
   }

   final MethodHandle methodHandle = buildAppropriateMethodHandle(chartChoice);
   try
   {
      chart =
        methodHandle != null
      ? (XYChart<String, Double>) methodHandle.invokeExact(this, xAxis, yAxis)
      : null;
      chart.setTitle('Average Department Salaries');
   }
   catch (WrongMethodTypeException wmtEx)
   {
      LOGGER.log(
         Level.SEVERE,
         'Unable to invoke method because it is wrong type - {0}',
         wmtEx.toString());
   }
   catch (Throwable throwable)
   {
      LOGGER.log(
         Level.SEVERE,
         'Underlying method threw a Throwable - {0}',
         throwable.toString());
   }

   return chart;
}

/**
 * Build a MethodHandle for calling the appropriate chart generation method
 * based on the provided ChartTypes choice of chart.
 * 
 * @param chartChoice ChartTypes instance indicating which type of chart
 *    is to be generated so that an appropriately named method can be invoked
 *    for generation of that chart.
 * @return MethodHandle for invoking chart generation.
 */
private MethodHandle buildAppropriateMethodHandle(final ChartTypes chartChoice)
{
   MethodHandle methodHandle = null;
   final MethodType methodDescription =
      MethodType.methodType(XYChart.class, Axis.class, Axis.class);
   final String methodName = 'generate' + chartChoice.getChartTypeName() + 'Chart';

   try
   {
      methodHandle =
         MethodHandles.lookup().findVirtual(
            this.getClass(), methodName, methodDescription);
   }
   catch (NoSuchMethodException | IllegalAccessException exception)
   {
      LOGGER.log(
         Level.SEVERE,
         'Unable to acquire MethodHandle to method {0} - {1}',
         new Object[]{methodName, exception.toString()});
   }
   return methodHandle;
}

随后的一系列图像显示了由JavaFX渲染时这些XY图表的显示方式。

面积图

条形图

气泡图

折线图

散点图

如上所述,方法句柄可能已经被用来进一步减少代码,因为用于生成每个XYChart单独方法不是绝对必要的,并且可以根据所需的图表类型进行反射式调用。 还值得强调的是,如果x轴数据是数字的,则对于所有的XYChart类型(包括气泡图),代码都是相同的(并且可以反射地调用)。

JavaFX使得生成代表所提供数据的有吸引力的图表变得容易。 Java 7功能使代码更简洁,更具表现力,并在适当的时候允许容易地应用反射,从而使此操作变得更加容易。

参考:来自JCG合作伙伴 Dustin Marx的JavaFX 2 XYCharts和Java 7功能,来自Inspired by Actual Events博客。

翻译自: https://www.javacodegeeks.com/2013/01/javafx-2-xycharts-and-java-7-features.html

java 和javafx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值