7 Embedding Swing Content in JavaFX Applications (Release 8) (original) (raw)
This article describes how to embed Swing components in JavaFX applications. It discusses the threading restrictions and provides working applications that illustrate Swing buttons with HTML content embedded in a JavaFX application and interoperability between Swing and JavaFX buttons.
The ability to embed JavaFX content in Swing applications has existed since the JavaFX 2.0 release. To enhance the interoperability of JavaFX and Swing, JavaFX 8 introduces a new class that provides reverse integration and enables developers to embed Swing components in JavaFX applications.
Before you run any code from this article, install JDK 8 on your computer.
SwingNode Class
JavaFX 8 introduces the SwingNode
class, which is located in the javafx.embed.swing
package. This class enables you to embed Swing content in a JavaFX application. To specify the content of the SwingNode object, call the setContent
method, which accepts an instance of the javax.swing.JComponent
class. You can call the setContent
method on either the JavaFX application thread or event dispatch thread (EDT). However, to access the Swing content, ensure that your code runs on EDT, because the standard Swing threading restrictions apply.
The code shown in Example 7-1 illustrates the general pattern of using the SwingNode
class.
Example 7-1
import javafx.application.Application; import javafx.embed.swing.SwingNode; import javafx.scene.Scene; import javafx.scene.layout.StackPane; import javafx.stage.Stage; import javax.swing.JButton; import javax.swing.SwingUtilities;
public class SwingFx extends Application {
@Override
public void start (Stage stage) {
final SwingNode swingNode = new SwingNode();
createSwingContent(swingNode);
StackPane pane = new StackPane();
pane.getChildren().add(swingNode);
stage.setTitle("Swing in JavaFX");
stage.setScene(new Scene(pane, 250, 150));
stage.show();
}
private void createSwingContent(final SwingNode swingNode) {
SwingUtilities.invokeLater(() -> {
swingNode.setContent(new JButton("Click me!"));
});
}
}
When run, this code produces the output shown in Figure 7-1.
Embedding Swing Content and Handling Events
The ButtonHtmlDemo
in the Swing tutorial adds font, color, and other formatting to three buttons shown in Example 7-2 and Example 7-3. The buttons respond to mouse and keyboard events as shown in Example 7-5 and Example 7-6. Figure 7-2 shows the three buttons created using Swing in the ButtonHtmlDemo
now embedded in a JavaFX Application (SwingNodeSample
). You will create the SwingNodeSample
application and ensure that all events are delivered to an appropriate Swing button and get processed.
The left and right buttons have multiple lines of text implemented with the HTML formatting as shown in Example 7-2.
Example 7-2
b1 = new JButton("Disable
"
+ "middle button",
leftButtonIcon);
b3 = new JButton("Enable
"
+ "middle button",
rightButtonIcon);
The simple format of middle button does not require HTML, so it is initialized with a string label and an image as shown in Example 7-3.
Example 7-3
b2 = new JButton("middle button", middleButtonIcon);
All three buttons have the tooltips and mnemonic characters as shown in Example 7-4.
Example 7-4
b1.setToolTipText("Click this button to disable the middle button."); b2.setToolTipText("This middle button does nothing when you click it."); b3.setToolTipText("Click this button to enable the middle button.");
b1.setMnemonic(KeyEvent.VK_D); b2.setMnemonic(KeyEvent.VK_M); b3.setMnemonic(KeyEvent.VK_E);
The left and right buttons are used to disable and enable the middle button respectively. To enable the application to detect and respond to user action on these buttons, attach action listeners and set action commands as shown in Example 7-5.
Example 7-5
b1.addActionListener(this); b3.addActionListener(this);
b1.setActionCommand("disable"); b3.setActionCommand("enable");
Implement the actionPerformed
method shown in Example 7-6. This method is called when the user clicks the left or right button.
Example 7-6
public void actionPerformed(ActionEvent e) { if ("disable".equals(e.getActionCommand())) { b2.setEnabled(false); b1.setEnabled(false); b3.setEnabled(true); } else { b2.setEnabled(true); b1.setEnabled(true); b3.setEnabled(false); } }
See the complete code of the [ButtonHtmlDemo.java](buttonhtmldemojava.htm#BGBEDJFJ)
class.
Now set up a JavaFX project and run the SwingNodeSample
application.
To create the SwingNodeSample
application:
Ensure that JDK 8 is installed on your computer. Then set up a JavaFX project in NetBeans IDE:
- From the File menu, choose New Project.
- In the JavaFX application category, choose JavaFX Application and click Next.
- Name the project SwingNodeSample and select a JavaFX platform based on JDK 8. Click Finish.
- In the Projects window, right-click the swingnodesample folder under Source Packages. Choose New and then choose Java class.
- Name a new class ButtonHtml and click Finish.
- Copy the code of the
[ButtonHtmlDemo.java](buttonhtmldemojava.htm#BGBEDJFJ)
class and paste it in the project. - Open the swingnodesample folder on your disk and create the images folder.
- Download the images
[left.gif](imagesources.htm#CHDFBCDB)
,[middle.gif](imagesources.htm#CHDCBJAI)
, and[right.gif](imagesources.htm#CHDHFCJJ)
by right clicking the image and selecting Save Image As, and save them in the images folder. - In the
SwingNodeSample
class, remove the code inside thestart
method that was automatically generated by NetBeans. - Instead, create a
SwingNode
object and implement thestart
method as shown in Example 7-7.
Example 7-7
@Override
public void start(Stage stage) {
final SwingNode swingNode = new SwingNode();
createSwingContent(swingNode);
StackPane pane = new StackPane();
pane.getChildren().add(swingNode);
Scene scene = new Scene(pane, 450, 100);
stage.setScene(scene);
stage.setTitle("ButtonHtmlDemo Embedded in JavaFX");
stage.show();
} - To embed the three buttons produced by the
ButtonHtml
class, set the content of the SwingNode object to be an instance of theButtonHtml
class as shown in Example 7-8.
Example 7-8
private void createSwingContent(final SwingNode swingNode) {
SwingUtilities.invokeLater(() -> {
swingNode.setContent(new ButtonHtml());
});
} - Press Ctrl (or Cmd) + Shift + I to correct the import statements.
To download the source code of the SwingNodeSample
application, click the SwingNodeSample.zip link.
Run the SwingNodeSample project and ensure that all means of interactivity provided for the buttons work as they should:
- With the mouse, hover over the buttons and see the tooltips.
- Click the left and right buttons to disable and enable the middle button respectively.
- Press Alt + D and Alt + E keys to disable and enable the middle button respectively.
Adding Interoperability Between Swing and JavaFX Components
You can provide interoperability between JavaFX buttons and Swing buttons. For example, the EnableFXButton
application shown in Figure 7-3 enables a user to click Swing buttons to disable or enable a JavaFX button. Conversely, the EnableButtons
application shown in Figure 7-4 enables a user to click a JavaFX button to activate a Swing button.
Using Swing Buttons to Operate a JavaFX Button
The EnableFXButton
application is created by modifying the SwingNodeSample
application and making the middle button an instance of the javafx.scene.control.Button
class. In the modified application, the Swing buttons (Disable FX button) and (Enable FX button) are used to disable and enable a JavaFX button (FX Button). Figure 7-3 shows the EnableFXButton
application.
Follow these steps to create the EnableFXButton
application:
From the File menu, choose New Project.
In the JavaFX application category, choose JavaFX Application and click Next.
Name the project EnableFXButton.
In the Projects window, right-click the enablefxbutton folder under Source Packages. Choose New and then choose Java class.
Name the new class ButtonHtml and click Finish.
Copy the code of the
[ButtonHtmlDemo.java](buttonhtmldemojava.htm#BGBEDJFJ)
class and paste it in the project.Change the package declaration to
enablefxbutton
.Open the enablefxbutton folder on your disk and create the images folder.
Download the images
[down.gif](imagesources.htm#CHDBEFBH)
and[middle.gif](imagesources.htm#CHDCBJAI)
by right clicking the image and selecting Save Image As, and save them in the images folder.In the
EnableFXButton
class, declare aButton
object as shown in Example 7-9.
Example 7-9
public class EnableFXButton extends Application {
public static Button fxbutton;Remove the code inside the
start
method that was automatically generated by NetBeans IDE and implement thestart
method as shown in Example 7-10.
Example 7-10
@Override
public void start(Stage stage) {
final SwingNode swingNode = new SwingNode();
createSwingContent(swingNode);
BorderPane pane = new BorderPane();
fxbutton = new Button("FX button");pane.setTop(swingNode);
pane.setCenter(fxbutton);
Scene scene = new Scene(pane, 300, 100);
stage.setScene(scene);
stage.setTitle("Enable JavaFX Button");
stage.show();
}
12. Add the import statement for the SwingNode
class as shown in Example 7-11.
Example 7-11
import javafx.embed.swing.SwingNode;
13. Implement the createSwingContent
method to set the content of the SwingNode object as shown in Example 7-12.
Example 7-12
private void createSwingContent(final SwingNode swingNode) {
SwingUtilities.invokeLater(() -> {
swingNode.setContent(new ButtonHtml());
});
}
14. Press Ctrl (or Cmd) + Shift + I to add an import statement to the javax.swing.SwingUtilities
class.
15. Replace the initialization of fxbutton
with the code shown in Example 7-13 to add an image and set a tooltip and style for the JavaFX button.
Example 7-13
Image fxButtonIcon = new Image(
getClass().getResourceAsStream("images/middle.gif"));
fxbutton = new Button("FX button", new ImageView(fxButtonIcon));
fxbutton.setTooltip(
new Tooltip("This middle button does nothing when you click it."));
fxbutton.setStyle("-fx-font: 22 arial; -fx-base: #cce6ff;");
16. Press Ctrl (or Cmd) + Shift + I to add the import statements shown in Example 7-14.
Example 7-14
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.control.Tooltip;
17. Open the ButtonHtml
class and remove all code related to the middle button b2
.
18. Use the down.gif
image for b1
(Disable FX button) and b3
(Enable FX button) buttons as shown in Example 7-15.
Example 7-15
ImageIcon buttonIcon = createImageIcon("images/down.gif");
b1 = new JButton("Disable
"
+ "FX button",
buttonIcon);
b3 = new JButton("Enable
"
+ "FX button",
buttonIcon);
19. Modify the actionPerformed
method to implement the disabling and enabling of fxbutton
as shown in Example 7-16. Note that the disabling and enabling of the JavaFX button must happen on the JavaFX application thread.
Example 7-16
@Override
public void actionPerformed(ActionEvent e) {
if ("disable".equals(e.getActionCommand())) {
Platform.runLater(() -> {
EnableFXButton.fxbutton.setDisable(true);
});
b1.setEnabled(false);
b3.setEnabled(true);
} else {
Platform.runLater(() -> {
EnableFXButton.fxbutton.setDisable(false);
});
b1.setEnabled(true);
b3.setEnabled(false);
}
}
20. Press Ctrl (or Cmd) + Shift + I to add the import statement shown in Example 7-17.
Example 7-17
import javafx.application.Platform;
21. Run the application and click the Swing buttons to disable and enable the JavaFX button, as shown in Figure 7-3.
Using a JavaFX Button to Operate a Swing Button
You can further modify the EnableFXButton
application and implement the setOnAction
method for the JavaFX button so that clicking the JavaFX button activates the Swing button. The modified application (EnableButtons
) is shown in Figure 7-4.
To create the EnableButtons
application:
- Copy the EnableFXButton project and save it under the EnableButtons name.
- Rename the
EnableFXButton
class toEnableButtons
and theenablefxbutton
package toenablebuttons
. - Correct the package statement in both the
ButtonHtml
andEnableButtons
classes. - Open the
EnableButtons
class and make thepane
an instance of theFlowPane
class as shown in Example 7-18.
Example 7-18
FlowPane pane = new FlowPane(); - Modify the initialization of the
fxButtonIcon
variable to use theleft.gif
image as shown in Example 7-19.
Example 7-19
Image fxButtonIcon = new Image(
getClass().getResourceAsStream("images/left.gif")); - Change the
fxbutton
text, tooltip, and font size and set thedisableProperty
to true as shown in Example 7-20.
Example 7-20
fxbutton = new Button("Enable JButton", new ImageView(fxButtonIcon));
fxbutton.setTooltip(
new Tooltip("Click this button to enable the Swing button."));
fxbutton.setStyle("-fx-font: 18 arial; -fx-base: #cce6ff;");
fxbutton.setDisable(true); - Implement the
setOnAction
method by using a lambda expression as shown in Example 7-21. Note that you must change Swing objects on event dispatch thread only.
Example 7-21
fxbutton.setOnAction(ActionEvent e) {
SwingUtilities.invokeLater(() -> {
ButtonHtml.b1.setEnabled(true);
});
fxbutton.setDisable(true);
}
});
Note:
Ignore the error mark that appears on the left of the line that enablesb1
. You will correct this error at step 11. - Press Ctrl (or Cmd) + Shift + I to add the import statement to the
javafx.event.ActionEvent
class. - Add the
swingNode
andfxbutton
objects to the layout container as shown in Example 7-22.
Example 7-22
pane.getChildren().addAll(swingNode, fxbutton); - Change the application title to "Enable Buttons Sample" as shown in Example 7-23.
Example 7-23
stage.setTitle("Enable Buttons Sample"); - Open the
ButtonHtml
class and change the modifier of theb1
button topublic static
. Notice that the error mark in theEnableButtons
class has disappeared. - Remove all code related to the
b3
button and the line that sets an action command forb1
. - Modify the
actionPerformed
method by using a lambda expression as shown in Example 7-24.
Example 7-24
@Override
public void actionPerformed(ActionEvent e) {
Platform.runLater(() -> {
EnableButtons.fxbutton.setDisable(false);
});
b1.setEnabled(false);
}
Conclusion
In this chapter you learned how to embed existing Swing components in JavaFX applications and provide interoperability between Swing and JavaFX objects. The ability to embed Swing content into JavaFX applications enables developers to migrate Swing applications that use complex third-party Swing components for which they do not have source code or applications that have legacy modules that exist only in maintenance mode.