利用JavaFX功能豐富Swing應用程式

混魔MJM發表於2017-08-18

在本章中你將學到如何在單個應用程式中混合Swing表格和JavaFX柱狀圖。

本章以一個Swing應用程式開始,提供了一個通過新增JavaFX功能來豐富Swing應用程式的樣例。

Swing應用程式樣例

許多現實世界的專案使用Swing應用程式來處理表格。你可以繼續使用已有的程式碼並好好利用JavaFX APIs。對於本例來說,你可以增加一個JavaFX柱狀圖,以此為表格資料提供一個彩色插圖。本章提供了SwingInterop例子,其中處理了一個Swing表格和一個JavaFX柱狀圖。當你改變表格裡面的資料時,柱狀圖會立刻更新。

從只包含Swing表格的樣例應用程式開始,如圖4-1。

圖4-1 Swing JTable應用程式視窗

13_4_1 jtable

這個應用程式包含兩個類

● SampleTableModel.java

● SwingInterop.java

SampleTableModel.java類繼承了AbstractTableModel類並定義了表格。

SwingInterop類繼承了JApplet類,是這個應用程式的基礎類。其main方法在事件分發執行緒(EDT)上呼叫了run方法,其中建立了一個JFrame物件和一個JApplet物件,並用SwingInterop類的例項初始化了JApplet物件。然後呼叫init方法,建立了表格並將其加入到applet的內容皮膚中。

整合JavaFX柱狀圖

為了給柱狀圖提供資料,修改SampleTableModel類,增加一個新的類變數(bcData)和一個新的方法來返回適合柱狀圖的資料格式。getBarChartData方法的實現如例4-1所示。

例4-1

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

@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

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

Platform.runLater(new Runnable() {
    @Override
    public void run() {
        createScene();
    }
});


在SwingInterop類中加入如下引入宣告,如例4-5所示。

4-5

import javafx.application.Platform;

實現SwingInterop類的createScene方法,如例4-6。增加引入宣告並定義chart 成員變數。

例4-6

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

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

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程式視窗

13_4_2 barchart

應用程式檔案

原始碼

● SampleTableModel.java

● SwingInterop.java

NetBeans工程

● SwingInterop.zip


原文連結:http://www.javafxchina.net/blog/2015/09/doc1304-fx-feature/

相關文章