3 将JavaFX集成到Swing应用程序中
本章介绍如何将JavaFX内容添加到Swing应用程序中,以及在单个应用程序中同时运行Swing和JavaFX内容时如何正确使用线程。
JavaFX SDK提供了位于javafx.embed.swing包中的JFXPanel类,它使您可以将JavaFX内容嵌入到Swing应用程序中。
将JavaFX内容添加到Swing组件中
为了本章的目的,您将创建一个JFrame组件,向其添加一个JFXPanel对象,并设置包含JavaFX内容的JFXPanel组件的图形场景。
与任何Swing应用程序一样,您需要在事件分派线程(EDT)上创建图形用户界面(GUI)。示例3-1显示了initAndShowGUI方法,该方法创建一个JFrame组件并向其添加一个JFXPanel对象。创建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() {
// 此方法在EDT线程上调用
JFrame frame = new JFrame("Swing和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) {
// 此方法在JavaFX线程上调用
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("欢迎使用JavaFX!");
root.getChildren().add(text);
return (scene);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
initAndShowGUI();
}
});
}
}
Swing-JavaFX互操作性和线程
在单个应用程序中同时存在JavaFX和Swing数据时,可能会遇到以下互操作性情况:
-
通过更改Swing数据触发JavaFX数据的更改。
-
通过更改JavaFX数据触发Swing数据的更改。
响应Swing数据更改而更改JavaFX数据
JavaFX数据应仅在JavaFX用户线程上访问。每当您必须更改JavaFX数据时,将您的代码封装到一个Runnable对象中,并调用Platform.runLater方法,如示例3-2所示。
响应JavaFX数据更改而更改Swing数据
Swing数据应仅在EDT上更改。为确保您的代码在EDT上执行,将其封装到一个Runnable对象中,并调用SwingUtilities.invokeLater方法,如示例3-3所示。
介绍SimpleSwingBrowser应用程序
为了了解Swing-JavaFX互操作性的工作原理,可以考虑使用SimpleSwingBrowser应用程序。这是一个带有集成JavaFX组件的Swing应用程序,用于查看网页。您可以在地址栏中输入URL并在应用程序窗口中查看加载的页面。 SimpleSwingBrowser应用程序窗口如图3-1所示。
初始化Swing数据
您可以查看SimpleSwingBrowser.java文件,或者下载包含NetBeans项目的SimpleSwingBrowser.zip文件。从zip文件中提取文件到本地文件系统上的一个目录,并在Netbeans IDE中运行该项目。
从版本7.2开始,NetBeans IDE支持嵌入JavaFX内容的Swing应用程序。创建新项目时,在JavaFX类别中选择Swing应用程序中的JavaFX。
注意:
要在防火墙后运行此应用程序,必须指定代理设置,以便应用程序可以访问远程资源。
在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");
}
});
}
SimpleSwingBrowser类初始化Swing对象,并调用initComponents方法创建GUI,如示例3-5所示。
示例 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("前往");
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();
}
}
该应用程序的顶层窗口是一个 JFrame 对象,其中包含各种 Swing 组件,如文本字段、按钮、进度条和用于显示 JavaFX 内容的 JFX 面板。
加载JavaFX内容
在第一次运行时,将http://oracle.com网页加载到WebView对象中。当在地址栏中输入新的URL时,动作监听器(附加在initComponents方法中的txtURL文本字段上)会启动页面加载,如示例3-6所示。
示例3-6
ActionListener al = new ActionListener() {
@Override public void actionPerformed(ActionEvent e) {
loadURL(txtURL.getText());
}
};
JavaFX数据只能在JavaFX应用程序线程上访问。loadURL方法将代码封装到一个Runnable对象中,并调用Platform.runLater方法,如示例3-7所示。
示例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,以作为标题放置在应用程序窗口上。这个行为在createScene方法中实现,如示例3-8所示。
示例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);
}
});
}
});
}
});
}

