javafx.embed.swing.JFXPanel
javafx.embed.swing.SwingNode
JFXPanel
is a Swing JComponent that can be used to add a JavaFX scene graph to a Swing component hierarchy. The SwingNode
is a JavaFX node that can be used to add Swing components to a JavaFX scene graph. These two classes allow integration between Swing and JavaFX in either direction: Swing can be integrated in JavaFX, and vice versa. Let’s take a look at both uses.JFXPanel
class provides the ability to add a JavaFX scene graph to a Swing application. The JFXPanel
extends the javax.swing.JComponent
class and is, therefore, a default Swing JComponent. Because of this, it can be added to any Swing component hierarchy, as shown in the following code snippet:JFrame
that is shown on the screen. The frame contains a Swing-based button that is defined by the JButton
class and an instance of the JFXPanel
class. I won’t discuss all the Swing internal mechanisms and how the Swing framework should be used; the important part is the JFXPanel
instance that is added to the Swing frame. This instance is currently empty, and when running the sample, only the Swing button will be shown in the application window. For the next step, you can use the JFXPanel
to add JavaFX controls to the Swing application, as shown here:StackPane
. The last line of the snippet sets the scene graph to the JFXPanel
. While this seems easy, there is a big problem: The code snippets shown cannot be mixed in a single thread. Swing operations need to be executed on the event dispatch thread (EDT), and all JavaFX operations need to be executed on the JavaFX application thread. Both Swing and JavaFX provide helpful methods to execute code on the needed thread. In Swing, you can use the SwingUtilites.invokeLater(Runnable doRun)
method to execute a runnable on the EDT.Platform.runLater(Runnable doRun).
Runnable
instances and executed on the right application threads. Let’s start with the Swing part and create a simple sample application that uses the code snippet that was shown previously:Runnable
lambda expression and executed on the EDT. To add the JavaFX scene graph to the sample, the JavaFX code must be executed on a different thread. The following code demonstrates how to achieve this:Runnable
and executed on the EDT. In this code block, the Platform.runLater(…)
method is used to execute a Runnable
on the JavaFX application thread. Here, the JavaFX button is defined. This approach greatly increases the complexity of the code but is necessary because both UI toolkits are defined as single-threaded toolkits, and therefore all the code that uses or modifies controls of the specific toolkit must be executed on the right thread. If you don’t do this, an exception will be thrown at run time because the JavaFX scene wasn’t initialized in the JavaFX application thread.ActionListener
instance to the Swing button will be executed in the EDT, but the code will affect the JavaFX button and change its disable
property. Therefore, the Platform.runLater(…)
method must be used here again. Now the code block will be executed in the JavaFX application thread and change the disable
property. A similar course of action is needed in the action handler of the JavaFX button. Here, SwingUtilities.invokeLater(…)
is used to change the enabled
bean property of the Swing button.NOTE
In the previous sample, the button instances are defined as static fields of the class. This is normally not a good architecture, but it is used here to create a simple demo application in only a few lines of code. Normally, an instance of a custom class should be created, and this class can then contain the UI and the controller logic of the application. |
JFXPanel
, individual parts of an application can be migrated one by one from Swing to JavaFX. This approach would also benefit you if you needed a special JavaFX control, such as the WebView
, the graph API, or the MediaView
, in a Swing application. Swing, for example, doesn’t contain components such as the WebView
or the MediaView
that are defined as basic nodes in JavaFX. The graph API is another part of JavaFX that has no complement in the Swing basic components. Integrating JavaFX in a Swing application would allow you to add a WebView
instance to a Swing application and show special HTML content onscreen. So, HTML5 and CSS3 can be rendered in an existing Swing app; only a few Platform.runLater(…)
calls are needed, and it won’t add to the complexity of the code too much.NOTE
As you will see later, there is an experimental JavaFX feature that can be used to remove all the threading issues and make the EDT the same thread as the JavaFX application thread. Because this feature is still experimental and a developer should still know about handling the two threads, the first approach is discussed here. |
JFXPanel
class extends the JComponent
class, and therefore all the default methods that are known from Swing can be found and used here. In addition, the class provides only the two methods getScene()
and setScene(…)
, which can be used to define and access the internal scene graph of the component.NOTE
In addition to the mentioned classes, JavaFX provides the javafx.embed.swing.SwingFXUtils class that contains a set of helpful methods when using Swing and JavaFX APIs in one application. Methods to convert BufferedImage instances to JavaFX Images, for example, can be found here. |
SwingNode
is a JavaFX node that can be used to integrate Swing components in a JavaFX application. As mentioned in the previous section, it is important to know about the different toolkit threads when working with the SwingNode
. The following example defines a JavaFX sample and adds a Swing button to the application:SwingNode
and integrates a Swing button. Therefore, a JButton
instance is created and defined as the content of the SwingNode
. The JButton
instance must be created on the EDT, and because of that, the creation is wrapped in a SwingUtilities.invokeLater(…)
method. Like in the demo of the JFXPanel
, both buttons can be used to modify the other one. Therefore, SwingUtilities.invokeLater(…)
and Platform.runLater(…)
calls are needed. Using these methods is equal to the functionalities covered with the JFXPanel
, so I won’t discuss that again here. As in the JFXPanel
example, the complete event handling of Swing and JavaFX is supported by the use of the SwingNode
.SwingNode
. Swing extends the old AWT UI toolkit, and therefore, heavyweight AWT components can be added to a Swing component hierarchy. If the hierarchy of Swing components that is defined as the content of a SwingNode
contains heavyweight components, the SwingNode
may fail to paint them.NOTE
As mentioned earlier in the book, only components that extend the Node class can be part of a JavaFX scene graph, but a Swing component doesn’t extend this class. So, how can a Swing component be added to an application with the help of the SwingNode? The SwingNode uses a BufferedImage internally to paint the Swing component into it. This BufferedImage will be shown in the JavaFX scene graph. As a result, the Swing JButton instance isn’t a real part of the scene graph. |
JFXPanel
demo and refactor it to a version that can be used once the singleThread
flag is set:Platform.runLater(…)
calls are removed. Because Swing and JavaFX share a single thread, only one SwingUtilities.invokeLater(…)
call is needed here. In the sample, the creation of the JFXPanel
instance is extracted to the main method and isn’t part of the lambda expression that is passed to the invokeLater(…)
method. By calling the constructor of the JFXPanel
, JavaFX will set up the single-threaded event-dispatching mechanism.SwingNode
or the JFXPanel
, you can mix Swing components and JavaFX controls. This is useful if a company needs to migrate a Swing application to JavaFX. For applications where custom Swing components were specifically developed for that application, a developer wouldn’t need to reimplement these custom components once migrated to JavaFX. The components could be still used in a JavaFX porting by using SwingNode
, or you could migrate and refactor the application so only a few views and dialogs will be re-created in JavaFX. Here, the JFXPanel
would be helpful because the new JavaFX views can be integrated in the given Swing application.BufferedImages
are used here to store the rendering of one toolkit and render it in the other one.NOTE
Some other third-party JavaFX themes have been released. AquaFX is one of them, and it can be used to imitate the look of native Mac OS controls. The platform-specific look and feel on Mac OS does the same in Swing, so on a Mac, these two themes can be used to create a more consistent look. Ideally, other JavaFX themes that do the same for other operating systems will follow. |
eclipse/plugins/org.eclipse.swt**.jar
. Because SWT depends on the OS, you will need different JARs depending on the operating system where the application will be running. For Mac OS, you can use the org.eclipse.swt.cocoa.macosx.x86_64_3.100.1.v4236b.jar
file that is defined in Eclipse Juno, for example.JAVA_HOME/jre/lib/jfxswt.jar.
This JAR must be added to the classpath too. Once this is done, you will find the following class: javafx.embed.swt.FXCanvas
.FXCanvas
class. This class extends the SWT Canvas
class and can be used anywhere that an SWT canvas can appear. I won’t take a deep dive into how SWT is internally working or what an SWT Canvas does. Instead, let’s focus on the basic features of the FXCanvas
class that are useful for SWT developers.FXCanvas
class can be used to integrate a scene graph into your SWT application; the class provides two methods: getScene()
and setScene(Scene scene)
. The class can be used like the JFXPanel that was shown earlier. The following example defines an SWT application that contains an SWT button and a JavaFX button that can interact with each other:Shell
and Display
classes. These classes are used to create an SWT application that contains one frame. The frame contains an SWT button and an FXCanvas
instance that is used as a wrapper around the JavaFX scene graph that holds the JavaFX button. Both buttons have an action listener defined that will handle their action events. Whenever the SWT button is clicked, the JavaFX button will change its disabled
property. When the user clicks the JavaFX button, the enabled bean property of the SWT button will change. Figure 8-3 shows how the application will look at run time.NOTE
To start the sample on a Mac, the special VM option -XstartOnFirstThread must be specified. |
NOTE
The jfxswt.jar file that contains the FXCanvas class provides some additional classes. One of them, the SWTFXUtils class, can be used to convert between org.eclipse.swt.graphics.ImageData instances and JavaFX Image instances. |
3.22.216.59