JavaFX - 统计图 - JavaFX教程

Screenshot AddressApp Part 6

第6部分的主题

  • 创建一个统计图显示生日的分布。

生日统计

在AddressApp中所有人员都有生日。当我们人员庆祝他们生日的时候,如果有一些生日的统计不是会更好。

我们使用柱状图,包含每个月的一个条形。每个条形显示在指定月份中有多少人需要过生日。

统计FXML视图

  1. ch.makery.address.view包中我们开始创建一个BirthdayStatistics.fxml(右击包|New|other..|New FXML Document) 生日统计FXML

  2. 在Scene Builder中打开BirthdayStatistics.fxml文件。

  3. 选择根节点AnchorPane。在Layout组中设置Pref Width为620,Pref Height为450。

  4. 添加BarChartAnchorPane中。

  5. 右击BarChart并且选择Fit to Parent

  6. 保存fxml文件,进入到Eclipse中,F5刷新项目。

在我们返回到Scene Builder之前,我们首先创建控制器,并且在我们的MainApp中准备好一切。

统计控制器

在view包 ch.makery.address.view中创建一个Java类,称为BirthdayStatisticsController.java

在开始解释之前,让我们看下整个控制器类。

BirthdayStatisticsController.java
package ch.makery.address.view;

import java.text.DateFormatSymbols;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.XYChart;
import ch.makery.address.model.Person;

/**
 * The controller for the birthday statistics view.
 * 
 * @author Marco Jakob
 */
public class BirthdayStatisticsController {

    @FXML
    private BarChart<String, Integer> barChart;

    @FXML
    private CategoryAxis xAxis;

    private ObservableList<String> monthNames = FXCollections.observableArrayList();

    /**
     * Initializes the controller class. This method is automatically called
     * after the fxml file has been loaded.
     */
    @FXML
    private void initialize() {
        // Get an array with the English month names.
        String[] months = DateFormatSymbols.getInstance(Locale.ENGLISH).getMonths();
        // Convert it to a list and add it to our ObservableList of months.
        monthNames.addAll(Arrays.asList(months));

        // Assign the month names as categories for the horizontal axis.
        xAxis.setCategories(monthNames);
    }

    /**
     * Sets the persons to show the statistics for.
     * 
     * @param persons
     */
    public void setPersonData(List<Person> persons) {
        // Count the number of people having their birthday in a specific month.
        int[] monthCounter = new int[12];
        for (Person p : persons) {
            int month = p.getBirthday().getMonthValue() - 1;
            monthCounter[month]++;
        }

        XYChart.Series<String, Integer> series = new XYChart.Series<>();

        // Create a XYChart.Data object for each month. Add it to the series.
        for (int i = 0; i < monthCounter.length; i++) {
            series.getData().add(new XYChart.Data<>(monthNames.get(i), monthCounter[i]));
        }

        barChart.getData().add(series);
    }
}

控制器如何工作

  1. 控制器需要从FXML文件中访问两个元素:

    • barChar:它有StringInteger类型。String用于x轴上的月份,Integer用于指定月份中人员的数量。
    • xAxis:我们使用它添加月字符串
  2. initialize() 方法使用所有月的列表填充x-axis

  3. setPersonData(…)方法将由MainApp访问,设置人员数据。它遍历所有人员,统计出每个月生日的人数。然后它为每个月添加XYChart.Data到数据序列中。每个XYChart.Data对象在图表中表示一个条形。

连接视图和控制器

  1. 在Scene Builder中打开BirthdayStatistics.fxml

  2. 在Controller组中设置BirthdayStatisticsController为控制器。

  3. 选择BarChart,并且选择barChar作为fx:id属性(在Code组中)

  4. 选择CategoryAxis,并且选择xAxis作为fx:id属性。 类别轴

  5. 你可以添加一个标题给BarChar(在Properties组中)进一步修饰。

连接View/Controller和MainApp

我们为生日统计使用与编辑人员对话框相同的机制,一个简单的弹出对话框。

添加下面的方法到MainApp类中

/**
 * Opens a dialog to show birthday statistics.
 */
public void showBirthdayStatistics() {
    try {
        // Load the fxml file and create a new stage for the popup.
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(MainApp.class.getResource("view/BirthdayStatistics.fxml"));
        AnchorPane page = (AnchorPane) loader.load();
        Stage dialogStage = new Stage();
        dialogStage.setTitle("Birthday Statistics");
        dialogStage.initModality(Modality.WINDOW_MODAL);
        dialogStage.initOwner(primaryStage);
        Scene scene = new Scene(page);
        dialogStage.setScene(scene);

        // Set the persons into the controller.
        BirthdayStatisticsController controller = loader.getController();
        controller.setPersonData(personData);

        dialogStage.show();

    } catch (IOException e) {
        e.printStackTrace();
    }
}

一切设置完毕,但是我们没有任何东西实际上调用新的showBirthdayStatistics()方法。幸运的是我们已经在RootLayout.fxml中有一个菜单,它可以用于这个目的。

显示生日统计菜单

RootLayoutController中添加下面的方法,它将处理显示生日统计菜单项的用户点击。

/**
 * Opens the birthday statistics.
 */
@FXML
private void handleShowBirthdayStatistics() {
  mainApp.showBirthdayStatistics();
}

现在,使用Scene Builder打开RootLayout.fxml文件。创建Staticstic 菜单,带有一个Show Statistcs MenuItem:

Show Statistics菜单

选择Show Statistics MenuItem,并且选择handleShowBirthdayStatistics作为On Action(在Code组中)。

Show Statistics On Action

进入到Eclipse,刷新项目,测试它。

JavaFX Chart的更多信息

更多信息的一个好地方是官方Oracle教程,使用JavaFX Chart.