DOC-13-04 利用JavaFX功能丰富Swing应用程序
在本章中你将学到如何在单个应用程序中混合Swing表格和JavaFX柱状图。
本章以一个Swing应用程序开始,提供了一个通过添加JavaFX功能来丰富Swing应用程序的样例。
Swing应用程序样例
许多现实世界的项目使用Swing应用程序来处理表格。你可以继续使用已有的代码并好好利用JavaFX APIs。对于本例来说,你可以增加一个JavaFX柱状图,以此为表格数据提供一个彩色插图。本章提供了SwingInterop例子,其中处理了一个Swing表格和一个JavaFX柱状图。当你改变表格里面的数据时,柱状图会立刻更新。
从只包含Swing表格的样例应用程序开始,如图4-1。
图4-1 Swing JTable应用程序窗口
这个应用程序包含两个类
SampleTableModel.java类继承了AbstractTableModel类并定义了表格。
SwingInterop类继承了JApplet类,是这个应用程序的基础类。其main方法在事件分发线程(EDT)上调用了run方法,其中创建了一个JFrame对象和一个JApplet对象,并用SwingInterop类的实例初始化了JApplet对象。然后调用init方法,创建了表格并将其加入到applet的内容面板中。
整合JavaFX柱状图
为了给柱状图提供数据,修改SampleTableModel类,增加一个新的类变量(bcData)和一个新的方法来返回适合柱状图的数据格式。getBarChartData方法的实现如例4-1所示。
例4-1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.scene.chart.BarChart; public class SampleTableModel extends AbstractTableModel { private static ObservableList<BarChart.Series> bcData; public ObservableList<BarChart.Series> getBarChartData() { if (bcData == null) { bcData = FXCollections.<BarChart.Series>observableArrayList(); for (int row = 0; row < getRowCount(); row++) { ObservableList<BarChart.Data> series = FXCollections.<BarChart.Data>observableArrayList(); for (int column = 0; column < getColumnCount(); column++) { series.add(new BarChart.Data(getColumnName(column), getValueAt(row, column))); } bcData.add(new BarChart.Series(series)); } } return bcData; } //rest of the SampleTableModel class code } |
SwingInterop类重写了JApplet.init方法,以创建应用程序的内容面板。修改init方法来创建一个保存JavaFX柱状图的JFXPanel对象和一个组织JavaFX图表和表格的JSplitPane对象。Init方法中所需要的修改如例4-2中的加粗代码所示。
例4-2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
@Override public void init() { tableModel = new SampleTableModel(); // create javafx panel for charts chartFxPanel = new JFXPanel(); chartFxPanel.setPreferredSize(new Dimension(PANEL_WIDTH_INT, PANEL_HEIGHT_INT)); //create JTable JTable table = new JTable(tableModel); table.setAutoCreateRowSorter(true); table.setGridColor(Color.DARK_GRAY); SwingInterop.DecimalFormatRenderer renderer = new SwingInterop.DecimalFormatRenderer(); renderer.setHorizontalAlignment(JLabel.RIGHT); for (int i = 0; i < table.getColumnCount(); i++) { table.getColumnModel().getColumn(i).setCellRenderer(renderer); } JScrollPane tablePanel = new JScrollPane(table); tablePanel.setPreferredSize(new Dimension(PANEL_WIDTH_INT, TABLE_PANEL_HEIGHT_INT)); JPanel chartTablePanel = new JPanel(); chartTablePanel.setLayout(new BorderLayout()); //Create split pane that holds both the bar chart and table JSplitPane jsplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); jsplitPane.setTopComponent(chartTablePanel); jsplitPane.setBottomComponent(tablePanel); jsplitPane.setDividerLocation(410); chartTablePanel.add(chartFxPanel, BorderLayout.CENTER); //Add the split pane to the content pane of the application add(jsplitPane, BorderLayout.CENTER); } |
为了避免语法错误,在SwingInterop类中增加一个引入声明和charFxPanel成员变量,如例4-3所示。
例4-3
1 2 3 4 5 6 7 |
import javafx.embed.swing.JFXPanel; import javax.swing.*; public class SwingInterop extends JApplet { private static JFXPanel chartFxPanel; // rest of the SwingInterop class code here } |
你已经准备好了用来呈现JavaFX数据的Swing应用程序的UI。下一步是创建JavaFX场景。因为JavaFX场景必须由JavaFX应用程序线程创建,将代码封装为一个Runnable对象,如例4-4所示。在init方法的末尾加入这些代码。
例4-4
1 2 3 4 5 6 |
Platform.runLater(new Runnable() { @Override public void run() { createScene(); } }); |
在SwingInterop类中加入如下引入声明,如例4-5所示。
例4-5
1 |
import javafx.application.Platform; |
实现SwingInterop类的createScene方法,如例4-6。增加引入声明并定义chart 成员变量。
例4-6
1 2 3 4 5 6 7 |
import javafx.scene.Scene; import javafx.scene.chart.Chart; private void createScene() { chart = createBarChart(); chartFxPanel.setScene(new Scene(chart)); } |
createBarChart方法创建了图表,并为表格添加了一个的改变监听器。注意所有JavaFX数据的改变都必须发生在JavaFX线程上。因此,将事件处理器中负责更新JavaFX图表的代码封装进一个Runnable对象中,并传入Platform.runLater方法。createBarChart方法的实现如例4-7所示。
例4-7
1 2 3 4 5 6 7 8 9 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 |
private BarChart createBarChart() { CategoryAxis xAxis = new CategoryAxis(); xAxis.setCategories(FXCollections.<String>observableArrayList(tableModel. getColumnNames())); xAxis.setLabel("Year"); double tickUnit = tableModel.getTickUnit(); NumberAxis yAxis = new NumberAxis(); yAxis.setTickUnit(tickUnit); yAxis.setLabel("Units Sold"); final BarChart chart = new BarChart(xAxis, yAxis, tableModel.getBarChartData()); tableModel.addTableModelListener(new TableModelListener() { public void tableChanged(TableModelEvent e) { if (e.getType() == TableModelEvent.UPDATE) { final int row = e.getFirstRow(); final int column = e.getColumn(); final Object value = ((SampleTableModel) e.getSource()).getValueAt(row, column); Platform.runLater(new Runnable() { public void run() { XYChart.Series<String, Number> s = (XYChart.Series<String, Number>) chart.getData().get(row); BarChart.Data data = s.getData().get(column); data.setYValue(value); } }); } } }); return chart; } |
增加如例4-8所示的引入声明。
例4-8
1 2 3 4 5 6 7 |
import javafx.collections.FXCollections; import javafx.scene.chart.BarChart; import javafx.scene.chart.CategoryAxis; import javafx.scene.chart.NumberAxis; import javafx.scene.chart.XYChart; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; |
重命名框架的标题为“Swing JTable and Bar Chart”并运行SwingInterop程序。
应用程序窗口如图4-2所示。
图4-2 SwingInterop程序窗口
应用程序文件
源代码
NetBeans工程

