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);
}
});
}
});
}
});
}


