4 JavaFXの機能を使用したSwingアプリケーションの強化
この章では、Swingの表とJavaFXの棒グラフを1つのアプリケーション内で組み合せる方法について学習します。
この章では、Swingアプリケーションを開始点として使用し、JavaFXの機能を追加することによって、このSwingアプリケーションを強化する方法の例を提供します。
サンプルSwingアプリケーション
実際の多くのプロジェクトでは、表を扱うSwingアプリケーションが使用されています。既存のコードをそのまま使用し、さらにJavaFX APIを活用できます。たとえば、JavaFXの棒グラフを追加して、表データをカラフルに表示できます。この章では、Swingの表とJavaFXの棒グラフを処理するSwingInterop
サンプルを提供します。表のセル内のデータを変更すると、棒グラフがすぐに更新されます。
開始点となるのは、図4-1に示す、Swingの表のみを含むサンプル・アプリケーションです。
このアプリケーションは、次の2つのクラスで構成されています。
SampleTableModel
クラスは、AbstractTableModel
クラスから継承されたクラスであり、表を定義します。
SwingInterop
クラスは、JApplet
クラスから継承するクラスであり、アプリケーションの基本クラスです。そのmain
メソッドは、イベント・ディスパッチ・スレッド(EDT)上でrun
メソッドをコールして、グラフィカル・ユーザー・インタフェース(GUI)を作成します。run
メソッドはJFrameオブジェクトとJAppletオブジェクトを作成し、SwingInterop
クラスのインスタンスを使用して、JAppletオブジェクトを初期化します。その後、init
メソッドをコールすることによって、表を作成し、その表をアプレットのコンテンツ・ペインに追加します。
前述のリンクをクリックすると、両方のクラスの実装を確認できます。
JavaFXの棒グラフの統合
棒グラフのデータを提供するには、新しいクラス変数(bcData
)と、表からデータを取得し、そのデータを棒グラフに適した形式で返すメソッドを追加することによって、SampleTableModel
クラスを変更します。例4-1に、getBarChartData
メソッドの実装を示します。
例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オブジェクトを作成します。例4-2に、init
メソッドに加える必要がある変更を太字で示します。
例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); }
構文エラーをなくすには、例4-3に示すように、import文とchartFxPanel
クラス変数の定義を、SwingInterop
クラスに追加します。
例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アプリケーション・スレッド上で作成する必要があるため、例4-4に示すように、コードをRunnableオブジェクト内にラップします。このコードをinit
メソッドの末尾に追加します。
例4-5に示すimport文をSwingInterop
クラスに追加します。
例4-6に示すように、SwingInterop
クラスのcreateScene
メソッドを実装します。import文を追加し、インスタンス変数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
メソッドに渡します。例4-7に、createBarChart
メソッドの実装を示します。
例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に示すimport文を追加します。
例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に、アプリケーションのウィンドウを示します。