DOC-13-03 在Swing应用程序中整合JavaFX
本章描述如何将JavaFX内容加入到Swing应用程序中,以及当Swing和JavaFX内容在单个应用中操作时如何正确的使用线程。
JavaFX SDK提供了JFXPanel类,位于javafx.embed.swing包中,可以使你在Swing应用程序中嵌入JavaFX内容。
在Swing组件中加入JavaFX内容
为了实现本章的目的,你将创建一个JFrame组件,添加一个JFXPanel对象,并为包含JavaFX内容的JFXPanel设置图像场景。
像任何Swing应用程序一样,你可以在事件调度线程(EDT)上创建图形用户界面(GUI)。例3-1展示了initAndShowGUI方法,其中创建了一个JFrame组件,添加了一个JFXPanel对象。创建一个JFXPanel类的实例隐式启动了JavaFX运行时。GUI创建后,调用initFX方法在JavaFX应用程序线程上创建JavaFX场景。
例3-1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
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协作性与线程
在JavaFX和Swing数据共存于单个应用程序中的时候,你可能会遇到以下协作性的情况:
● Swing数据的变化导致JavaFX数据变化
● JavaFX数据的变化导致Swing数据变化
响应Swing数据变化改变JavaFX数据
JavaFX数据应只在JavaFX用户线程中被访问。当你必须要改变JavaFX数据时,将代码封装为Runnable对象并调用Platform.runLater方法,如例3-2所示。
例3-2
1 2 3 4 5 6 7 8 9 10 |
jbutton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Platform.runLater(new Runnable() { @Override public void run() { fxlabel.setText("Swing button clicked!"); } }); } }); |
响应JavaFX数据变化改变Swing数据
Swing数据应只在事件调度线程中被访问。为了确保你的代码是在EDT中实现,将其封装进Runnable对象中并调用SwingUtilities.invokeLater方法,如例3-3所示
例3-3
1 2 3 4 5 6 |
SwingUtilities.invokeLater(new Runnable() { @Override public void run() { //Code to change Swing data. } }); |
介绍SimpleSwingBrowser应用程序
为了学习Swing-JavaFX协作性是如何工作的,参看SimpleSwingBrowser应用程序。这是一个整合了JavaFX浏览网页组件的Swing应用程序。你可以在地址栏中输入一个URL并查看应用程序窗口中载入的页面。SimpleSwingBrowser应用程序的窗口如图3-1所示。
图3-1 SimpleSwingBrowser应用程序窗口
初始化Swing数据
你可以查看SimpleSwingBrowser.java文件或者包含NetBeans工程的SimpleSwingBrowser.zip文件。解压zip文件到本地路径并在NetBeans IDE中运行工程。
在7.2版本中,NetBeans提供了对嵌入JavaFX内容的Swing应用程序的支持。当创建一个新的工程时,在JavaFX目录中选择JavaFX in Swing Application。
注意:为了在防火墙内运行这个应用程序,你必须指定代理设置使应用程序能访问远程资源。
在NetBeans IDE中,在工程窗口中右击SimpleSwingBrowser工程,选择Properties,在弹出的Project Properties对话框中,选择Run。
在VM Option中,设置如下格式的代理:-Dhttp.proxyHost=webcache.mydomain.com -Dhttp.proxyPort=8080
当程序开始时,SimpleSwingBrowser应用程序的GUI在EDT中创建。main方法实现如例3-4所示。
例3-4
1 2 3 4 5 6 7 8 9 10 11 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
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(); } } |
应用程序最顶层的窗口是JFrame对象,其中包含多个Swing组件,如文本框,按钮,进度条和一个用来显示JavaFX内容的JFX面板。
加载JavaFX内容
在第一次运行时,http://oracle.com的网页被载入到一个WebView对象当中。当在地址栏输入新的URL时,在initComponents方法中被附加txtURL文本框上的动作监听器将初始化页面的载入。如例3-6所示。
例3-6
1 2 3 4 5 |
ActionListener al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { loadURL(txtURL.getText()); } }; |
JavaFX数据应只在JavaFX应用程序线程中被访问。loadURL方法将代码封装进一个Runnable对象并调用Platform.runLater方法,如例3-7所示。
例3-7
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
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); } }); } }); } }); } |
应用程序文件
源文件
Netbeans工程

