3 Integrating JavaFX into Swing Applications
This chapter describes how to add JavaFX content into a Swing application and how to use threads correctly when both Swing and JavaFX content operate within a single application.
JavaFX SDK provides the JFXPanel
class, which is located in the javafx.embed.swing
package and enables you to embed JavaFX content into Swing applications.
Adding JavaFX Content to a Swing Component
For the purpose of this chapter, you create a JFrame
component, add a JFXPanel
object to it, and set the graphical scene of the JFXPanel component that contains JavaFX content.
As in any Swing application, you create the graphical user interface (GUI) on an event dispatch thread (EDT). Example 3-1 shows the initAndShowGUI
method, which creates a JFrame
component and adds a JFXPanel
object to it. Creating an instance of the JFXPanel
class implicitly starts the JavaFX runtime. After the GUI is created, call the initFX
method to create the JavaFX scene on the JavaFX application thread.
Example 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 Interoperability and Threads
With JavaFX and Swing data coexisting in a single application, you may encounter the following interoperability situations:
-
A JavaFX data change is triggered by a change in Swing data.
-
A Swing data change is triggered by a change in JavaFX data.
Changing JavaFX Data in Response to a Change in Swing Data
JavaFX data should be accessed only on the JavaFX User thread. Whenever you must change JavaFX data, wrap your code into a Runnable object and call the Platform.runLater
method as shown in Example 3-2.
Changing Swing Data in Response to a Change in JavaFX Data
Swing data should be changed only on the EDT. To ensure that your code is implemented on the EDT, wrap it into a Runnable object and call the SwingUtilities.invokeLater
method as shown in Example 3-3.
Introducing the SimpleSwingBrowser Application
To see how Swing–JavaFX interoperability works, consider the SimpleSwingBrowser
application. This is a Swing application with an integrated JavaFX component intended to view Web pages. You can type a URL in an address bar and view the page loaded in the application window. The SimpleSwingBrowser
application window is shown in Figure 3-1.
Figure 3-1 The SimpleSwingBrowser Application Window

Description of "Figure 3-1 The SimpleSwingBrowser Application Window"
Initializing Swing Data
You can view the SimpleSwingBrowser.java
file or download the SimpleSwingBrowser.zip
file with a NetBeans project. Extract files from the zip file to a directory on your local file system and run the project in your Netbeans IDE.
As of version 7.2, the NetBeans IDE provides support for Swing applications with the embedded JavaFX content. When creating a new project, in the JavaFX category choose JavaFX in Swing Application.
Note:
To run this application from behind a firewall, you must specify proxy settings in order for the application to access a remote resource.
In the NetBeans IDE, right-click the SimpleSwingBrowser project in the Projects window, select Properties, and in the Projects Properties dialog, select Run.
In the VM Options field, set the proxy in the following format:
-Dhttp.proxyHost=webcache.mydomain.com -Dhttp.proxyPort=8080
The GUI of the SimpleSwingBrowser
application is created on the EDT when the application starts. The main
method is implemented as shown in Example 3-4.
Example 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"); } }); }
The SimpleSwingBrowser
class initializes Swing objects and calls the initComponents
method to create the GUI as shown in Example 3-5.
Example 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(); } }
The topmost window of this application is a JFrame object, which contains various Swing components such as a text field, a button, a progress bar, and a JFX panel intended to display JavaFX content.
Loading JavaFX Content
On the first run, the web page at http://oracle.com is loaded into a WebView object. As a new URL is entered in the address bar, the action listener, which is attached to the txtURL
text field in the initComponents
method, initiates the loading of a page as shown in Example 3-6.
Example 3-6
ActionListener al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { loadURL(txtURL.getText()); } };
JavaFX data should only be accessed on the JavaFX application thread. The loadURL
method wraps the code into a Runnable object and calls the Platform.runLater
method as shown in Example 3-7.
Example 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; } }
Updating Swing Data
As a new page is loaded into the WebView component, the title of the page is retrieved from the JavaFX data and passed to the Swing GUI to be placed on the application window as a title. This behavior is implemented in the createScene
method as shown in Example 3-8.
Example 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); } }); } }); } }); }