image
CHAPTER
8
Integrating JavaFX, Swing, and SWT
In previous chapters, I used small JavaFX samples and applications to demonstrate JavaFX APIs and controls and showed how to create JavaFX applications by applying various functionalities and classes. Using these APIs, you’ll master the basics necessary to create applications, and these APIs will enable larger-scale tasks for your work with JavaFX, such as creating business applications that use background tasks and that communicate with a server application or a database. In some cases, you may already have a desktop application that was created with Swing or SWT. If you want to migrate these applications to JavaFX or add some JavaFX features, you will find helpful classes and methods in JavaFX that enable interoperability between JavaFX and Swing/SWT. This chapter will show these parts of the JavaFX framework along with some best practices and use cases.
Combining JavaFX and Swing
JavaFX provides two classes that can be used to create an application that mixes JavaFX and Swing:
imagejavafx.embed.swing.JFXPanel
imagejavafx.embed.swing.SwingNode
The 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.
Using the JFXPanel
With the help of the JFXPanel, you can enrich an existing Swing application with JavaFX. Therefore, the 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:
image
image
This code defines a Swing 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:
image
image
This code creates a JavaFX scene graph, which contains a JavaFX button that is wrapped in a 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.
JavaFX’s comparable method is Platform.runLater(Runnable doRun).
With the help of these methods, the code snippets shown can be wrapped in 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:
image
image
In the sample, the complete code that is Swing specific is wrapped in a 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:
image
image
The sample creates a Swing frame that contains a Swing-based button and a JavaFX button, as shown in Figure 8-1.
image
image
image
FIGURE 8-1.Mixing Swing and JavaFX
As you can see in the sample code, you now have a block that is wrapped in a 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.
image
image
Handling both UI threads will become even more complex when interaction between the two UI toolkits is needed. In the following example, an action listener is added to the Swing button that will influence the JavaFX button, and an action handler is defined for the JavaFX button that will modify the Swing one:
image
image
The JavaFX and Swing toolkits both define that the code of a handler or listener will always be executed on the correct UI thread. Therefore, the lambda expression that is added as an 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.
image
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.
As you can see, mixing both UI toolkits isn’t trivial. So, why would you use this approach? You would do this if a given Swing application can’t be migrated to JavaFX in only one step. By using the 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.
image
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.
The 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.
image
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.
Using the SwingNode
The 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:
image
image
In the example, a JavaFX application is created like in most other examples in this book, but this sample uses the 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.
It is important to use only lightweight Swing components in a 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.
image
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.
Using the Experimental Single-Thread Mode
JavaFX provides an experimental mode to sync the EDT and JavaFX application thread. In this mode, both UI toolkits will share a single thread, and all events of both toolkits that normally will be handled on the specific UI thread will be on a single thread. To activate the experimental mode, you must set an environment property when starting the application:
image
image
Once an application is started with this property, Swing and JavaFX will use just one thread, making the EDT the same thread as the JavaFX application thread. Doing this decreases the complexity of the examples shown so far. Let’s take a look at the JFXPanel demo and refactor it to a version that can be used once the singleThread flag is set:
image
image
As you can see in the code, all the 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.
Pros and Cons of the Integration
Mixing JavaFX and Swing brings both benefits and also potential issues. Let’s start with the positive effects. By using the 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.
You should also be aware of potential problems that must be managed when doing such a migration. As mentioned, the complexity of the code will rise when two different UI toolkits are used. The main concepts of these toolkits are different, and therefore a developer must be an expert in both of them to manage the migration and its issues. In addition, the visual representation of JavaFX and Swing is completely different. JavaFX uses the so-called Modena theme to render its controls onscreen. Modena is a cross-platform theme that is used on every OS. Swing contains cross-platform themes like Metal or Nimbus, but these themes define a completely different look than Modena does. A user will always notice this break in the UI because the application will look inconsistent. In addition to Modena, JavaFX can use the Caspian theme that was already defined in JavaFX 2. Swing provides a platform-specific look and feel that can define the visualization of all Swing-based components as they would be rendered by the underlying OS. Often, this look and feel includes native code to render the skin of the Swing components directly by the operating system, but there is no match in any of them. Figure 8-2 shows how a button is rendered by the different JavaFX themes and Swing look and feel.
image
image
image
FIGURE 8-2.Different representations of JavaFX and Swing
Additionally, mixing both UI toolkits can result in lags in performance. As mentioned, BufferedImages are used here to store the rendering of one toolkit and render it in the other one.
image
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.
Combining JavaFX and SWT
SWT is the default UI toolkit of the Eclipse IDE. It uses the native OS controls, so it cannot easily be configured to use custom UI features like you can do with JavaFX controls. Therefore, JavaFX provides a set of helpful classes that can be used to integrate JavaFX in SWT-based applications.
Because SWT is not part of the JDK or JRE, the integration is not as easy as it is with Swing. First, the SWT JAR must be added to the Java classpath. Since developers typically want to integrate JavaFX into an existing SWT application, this would already be arranged. If you still need the SWT JAR file, you can download it from the SWT release page (www.eclipse.org/swt/), or you can find it as part of an Eclipse installation at 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.
In addition to the SWT JAR, specific JavaFX classes are needed. Because SWT is not part of the JDK, the complete SWT support of JavaFX is defined in a special JAR that is not part of the default Java class path. In a JDK 8 installation, the JAR can be found under 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.
Using the FXCanvas
All you need to integrate JavaFX with SWT is the 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.
The 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:
image
image
This code uses some SWT-specific classes such as the 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.
image
image
image
FIGURE 8-3.AN SWT application with an internal JavaFX button
image
NOTE
To start the sample on a Mac, the special VM option -XstartOnFirstThread must be specified.
When looking at the example, you will see a great benefit compared to the interoperability with Swing. When mixing SWT and JavaFX, the SWT event dispatch thread and the JavaFX application thread are automatically the same, so you will not need to handle two different threads like with Swing and JavaFX.
image
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.
Because SWT uses the native OS to render the GUI components, these components will almost always look like native controls. Therefore, you will have the same problems with this approach as when mixing Swing and JavaFX: The controls will look different. As you can see in Figure 8-3, an SWT button doesn’t look like a JavaFX button. So, it is a best practice to use this mix only for custom controls or controls that can’t be created using SWT.
Summary
JavaFX provides some helpful classes to create interoperability with Swing or SWT. These classes can be used to migrate big applications, but it’s important to remain aware of the potential issues raised when mixing two different toolkits. If a mix is needed, choose wisely, and always consider how users will be impacted by visual inconsistencies in the UI.
I recommend using these techniques sparingly. The complexity of an application will always increase when you add a second UI toolkit. While it’s fine to create some intermediate results in a migration that contains Swing and JavaFX controls, for example, the final goal of an application is to target only one UI toolkit. Issues aside, having JavaFX offer these classes is a great option when you need to mix JavaFX with other UI toolkits.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.41.148