JavaFX for Swing Developers

Previous
Next

2 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 2-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 2-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 2-2.

Example 2-2

jbutton.addActionListener(new ActionListener() { 
    public void actionPerformed(ActionEvent e) { 
        Platform.runLater(new Runnable() { 
            @Override
            public void run() {
                fxlabel.setText("Swing button clicked!"); 
            }
        });
    }
});

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 2-3.

Example 2-3

SwingUtilities.invokeLater(new Runnable() {
    @Override
    public void run() {
        //Code to change Swing data.
    }
});

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 2-1.

Figure 2-1 The SimpleSwingBrowser Application Window

Description of Figure 2-1 follows
Description of "Figure 2-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 2-4.

Example 2-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 2-5.

Example 2-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 2-6.

Example 2-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 2-7.

Example 2-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 behavor is implemented in the createScene method as shown in Example 2-8.

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

Deploying Swing-JavaFX Applications

As of JavaFX 2.2, you can use the same deployment approach to package Swing-JavaFX applications that you would for pure JavaFX applications. Using JavaFX Ant tasks, you only need to set an attribute that the application's primary UI toolkit is Swing. The resulting package provides support for the same set of execution modes as a package for a JavaFX application; in other words, the application can be run standalone, using Web Start, embedded into a web page, or distributed as a self-contained application bundle. For more information, see the Deploying JavaFX Applications guide.

Previous
Next