3 SwingアプリケーションへのJavaFXの統合
この章では、JavaFXコンテンツをSwingアプリケーションに追加する方法と、1つのアプリケーション内でSwingおよびJavaFXコンテンツの両方を動作させるときにスレッドを正しく使用する方法について説明します。
JavaFX SDKでは、JFXPanel
クラスが提供されます。このクラスはjavafx.embed.swing
パッケージに含まれており、これを使用すると、JavaFXコンテンツをSwingアプリケーションに埋め込むことができるようになります。
SwingコンポーネントへのJavaFXコンテンツの追加
この章で使用するために、JFrame
コンポーネントを作成し、そこにJFXPanel
オブジェクトを追加した後、JavaFXコンテンツを含むJFXPanelコンポーネントのグラフィカル・シーンを設定します。
他のSwingアプリケーションの場合と同様、グラフィカル・ユーザー・インタフェース(GUI)はイベント・ディスパッチ・スレッド(EDT)上で作成します。例3-1に、JFrame
コンポーネントを作成し、そこにJFXPanel
オブジェクトを追加する、initAndShowGUI
メソッドを示します。JFXPanel
クラスのインスタンスを作成すると、JavaFXランタイムが暗黙的に起動します。GUIの作成後、initFX
メソッドをコールして、JavaFXアプリケーション・スレッド上でJavaFXシーンを作成します。
例3-1
import javafx.application.Platform; import javafx.embed.swing.JFXPanel; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.Text; import javax.swing.JFrame; import javax.swing.SwingUtilities; public class Test { private static void initAndShowGUI() { // This method is invoked on the EDT thread JFrame frame = new JFrame("Swing and JavaFX"); final JFXPanel fxPanel = new JFXPanel(); frame.add(fxPanel); frame.setSize(300, 200); frame.setVisible(true); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); Platform.runLater(new Runnable() { @Override public void run() { initFX(fxPanel); } }); } private static void initFX(JFXPanel fxPanel) { // This method is invoked on the JavaFX thread Scene scene = createScene(); fxPanel.setScene(scene); } private static Scene createScene() { Group root = new Group(); Scene scene = new Scene(root, Color.ALICEBLUE); Text text = new Text(); text.setX(40); text.setY(100); text.setFont(new Font(25)); text.setText("Welcome JavaFX!"); root.getChildren().add(text); return (scene); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { initAndShowGUI(); } }); } }
SwingとJavaFXの相互運用性とスレッド
1つのアプリケーション内でJavaFXとSwingのデータが共存する場合、相互運用性に関する次の状況が発生する可能性があります。
-
JavaFXデータの変更がSwingデータの変更によってトリガーされる。
-
Swingデータの変更がJavaFXデータの変更によってトリガーされる。
Swingデータの変更に応じたJavaFXデータの変更
JavaFXデータには、JavaFXユーザー・スレッド上でのみアクセスする必要があります。JavaFXデータを変更する必要がある場合、例3-2に示すように、コードをRunnableオブジェクト内にラップし、Platform.runLater
メソッドをコールします。
JavaFXデータの変更に応じたSwingデータの変更
Swingデータは、EDT上でのみ変更する必要があります。コードがEDT上で実装されるようにするには、例3-3に示すように、コードをRunnableオブジェクト内にラップし、SwingUtilities.invokeLater
メソッドをコールします。
SimpleSwingBrowserアプリケーションについて
SwingとJavaFXの相互運用性がどのように実現されるかを理解するために、SimpleSwingBrowser
アプリケーションについて考えてみましょう。これは、Webページを表示するためのJavaFXコンポーネントが統合されたSwingアプリケーションです。URLをアドレス・バーに入力すると、ロードされたページをアプリケーション・ウィンドウに表示できます。SimpleSwingBrowser
アプリケーションのウィンドウを図3-1に示します。
Swingデータの初期化
SimpleSwingBrowser.java
ファイルを表示するか、SimpleSwingBrowser.zip
ファイルをNetBeansプロジェクトとともにダウンロードできます。ファイルをzipファイルからローカル・ファイルシステム上のディレクトリに抽出し、プロジェクトをNetbeans IDE内で実行します。
NetBeans IDEのバージョン7.2以降、JavaFXコンテンツを埋め込んだSwingアプリケーションがサポートされます。新しいプロジェクトの作成時に、「JavaFX」カテゴリの「JavaFX in Swingアプリケーション」を選択します。
注意:
ファイアウォールの内側からこのアプリケーションを実行する場合、アプリケーションがリモート・リソースにアクセスできるように、プロキシ設定を指定する必要があります。
NetBeans IDEで、「プロジェクト」ウィンドウのSimpleSwingBrowserプロジェクトを右クリックし、「プロパティ」を選択した後、「プロジェクト・プロパティ」ダイアログで、「実行」を選択します。
「VMオプション」フィールドで、次の形式のプロキシを設定します。
-Dhttp.proxyHost=webcache.mydomain.com -Dhttp.proxyPort=8080
SimpleSwingBrowser
アプリケーションのGUIは、アプリケーションの起動時にEDT上で作成されます。main
メソッドは、例3-4に示すように実装されます。
例3-4
public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { SimpleSwingBrowser browser = new SimpleSwingBrowser(); browser.setVisible(true); browser.loadURL("http://oracle.com"); } }); }
例3-5に示すように、SimpleSwingBrowser
クラスは、Swingオブジェクトを初期化し、initComponents
メソッドをコールしてGUIを作成します。
例3-5
public class SimpleSwingBrowser extends JFrame { private final JFXPanel jfxPanel = new JFXPanel(); private WebEngine engine; private final JPanel panel = new JPanel(new BorderLayout()); private final JLabel lblStatus = new JLabel(); private final JButton btnGo = new JButton("Go"); private final JTextField txtURL = new JTextField(); private final JProgressBar progressBar = new JProgressBar(); public SimpleSwingBrowser() { super(); initComponents(); } private void initComponents() { createScene(); ActionListener al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { loadURL(txtURL.getText()); } }; btnGo.addActionListener(al); txtURL.addActionListener(al); progressBar.setPreferredSize(new Dimension(150, 18)); progressBar.setStringPainted(true); JPanel topBar = new JPanel(new BorderLayout(5, 0)); topBar.setBorder(BorderFactory.createEmptyBorder(3, 5, 3, 5)); topBar.add(txtURL, BorderLayout.CENTER); topBar.add(btnGo, BorderLayout.EAST); JPanel statusBar = new JPanel(new BorderLayout(5, 0)); statusBar.setBorder(BorderFactory.createEmptyBorder(3, 5, 3, 5)); statusBar.add(lblStatus, BorderLayout.CENTER); statusBar.add(progressBar, BorderLayout.EAST); panel.add(topBar, BorderLayout.NORTH); panel.add(jfxPanel, BorderLayout.CENTER); panel.add(statusBar, BorderLayout.SOUTH); getContentPane().add(panel); setPreferredSize(new Dimension(1024, 600)); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); } }
このアプリケーションの最上部のウィンドウは、テキスト・フィールド、ボタン、進行状況バー、JavaFXコンテンツを表示するためのJFXパネルなど、様々なSwingコンポーネントを含むJFrameオブジェクトです。
JavaFXコンテンツのロード
初回実行時には、http://oracle.comのWebページがWebViewオブジェクト内にロードされます。新しいURLをアドレス・バーに入力すると、initComponents
メソッドのtxtURL
テキスト・フィールドにアタッチされたアクション・リスナーが、例3-6に示すように、ページのロードを開始します。
例3-6
ActionListener al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { loadURL(txtURL.getText()); } };
JavaFXデータには、JavaFXアプリケーション・スレッド上でアクセスする必要があります。例3-7に示すように、loadURL
メソッドは、コードをRunnableオブジェクト内にラップし、Platform.runLater
メソッドをコールします。
例3-7
public void loadURL(final String url) { Platform.runLater(new Runnable() { @Override public void run() { String tmp = toURL(url); if (url == null) { tmp = toURL("http://" + url); } engine.load(tmp); } }); } private static String toURL(String str) { try { return new URL(str).toExternalForm(); } catch (MalformedURLException exception) { return null; } }
Swingデータの更新
新しいページをWebViewコンポーネント内にロードするときに、ページのタイトルがJavaFXデータから取得されてSwing GUIに渡され、アプリケーション・ウィンドウ上にタイトルとして配置されます。この動作は、例3-8に示すように、createScene
メソッド内に実装されます。
例3-8
private void createScene() { Platform.runLater(new Runnable() { @Override public void run() { WebView view = new WebView(); engine = view.getEngine(); engine.titleProperty().addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue<? extends String> observable, String oldValue, final String newValue) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { SimpleSwingBrowser.this.setTitle(newValue); } }); } }); } }); }