Integrating JavaFX 2.x and Swing: It's Simple
With JavaFX 1.x, it was possible to embed Swing controls into a JavaFX application, but officially it wasn't possible to do the reverse. Although Sun/Oracle didn't support it, clever people found a way around this (read my past writing on the subject if you're interested). However, it quickly became evident that many people had large, complex Swing applications that they weren't willing or capable of entirely refactoring as a JavaFX application. So, Oracle added the ability to embed JavaFX components (built-in controls, custom controls, as well as entire JavaFX Scenes) inside a Swing application. With the JavaFX 2.x JFXPanel class, the one class needed to accomplish this, you can host JavaFX components alongside Swing components within a Swing JPanel, for instance. Let's look at how this works conceptually:
Containment model:
JPanel <- JFXPanel <- Scene <- Group <- javafx.control.*
Or, in pseudocode:
JPanel.add(JFXPanel.setScene(Scene.setRoot(Group.getChildren().add( javafx.control ))));
That may look complicated, but it's really not. You set up the JavaFX Scene relationship as usual, with no need for a Stage since the Swing application will act as your stage. Instead, create your JavaFX container (HBox, VBox, or Group, and so on), set it as your Scene's Root (or Parent), then add JavaFX controls to that root container. Finally, add the Scene to a special JFXPanel class, whose only role in life is to be placed within a Swing container (i.e., a JPanel) and serve as a bridge between the two worlds — a wormhole, if you will. Voila… Swing/JavaFX integration in a nutshell.
A Working Example
As an example, let's take the really cool SizeView container I created with JavaFX 2.x a few months ago. This custom container allows you to place any number of JavaFX controls within it, where each are separated by a sizing bar. When you resize one component, the others adjacent to it resize accordingly. Learn all about the control and resizing algorithms from my past blog.
The code is below. The explanation is this:
- The Swing application starts
- A
JFrameis created and sized - The
SwingFX2class (which extendsJPanel) is instantiated and added to theJFrame - In the constructor,
initComponentsis called, where the following steps occur:
– AJFXPanelis created and added to theJPanel
– A SwingJButtonis created and added to theJPanel
– A Scene is created and provided a parent container (Group)
– A JavaFX Control is created and added to the root container
– The scene is added to theJFXPanel
– Everything is made visible
The application, when run, looks like this:
For the official Oracle tutorial on the JFXPanel class, go here.
Happy Coding!
-EJB
public class SwingFX2 extends JPanel { // It's a Swing JPanel
private JFXPanel jfxPanel; // The JavaFX component(s)
private JButton swingButton; // The Swing component
public SwingFX2(){
initComponents();
}
public static void main(String ...args){
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Create a Swing Frame
final JFrame frame = new JFrame();
frame.setMinimumSize(new Dimension(640, 480));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Add the Swing JPanel and make visible
frame.getContentPane().add(new SwingFX2());
frame.setVisible(true);
}
});
}
private void initComponents(){
// The JavaFX 2.x JFXPanel makes the Swing integration seamless
jfxPanel = new JFXPanel();
// Create the JavaFX Scene
createScene();
setLayout(new BorderLayout());
add(jfxPanel, BorderLayout.CENTER);
swingButton = new JButton();
swingButton.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent ae) {
System.exit(0);
}
});
swingButton.setText("Close");
add(swingButton, BorderLayout.SOUTH);
}
private void createScene() {
// The Scene needs to be created on "FX user thread", NOT on the
// AWT Event Thread
PlatformImpl.startup(
new Runnable() {
public void run() {
Group root = new Group();
Scene scene = new Scene(root, 80, 20);
SizeView sizeview = createSizeView(scene);
root.getChildren().add(sizeview);
jfxPanel.setScene(scene);
}
});
}
private SizeView createSizeView(Scene scene) {
double HBOX_WIDTH = scene.getWidth() - 10 - 10;
final SizeView sizeview = new SizeView();
sizeview.setLayoutX(10);
sizeview.setLayoutY(10);
sizeview.setPrefWidth(HBOX_WIDTH);
Node[] controls = new Node[5];
for ( int l = 0; l < controls.length; l++ ) {
controls[l] = new ListView();
}
sizeview.getChildren().setAll(controls);
scene.widthProperty().addListener(
new ChangeListener() {
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
Double w = (Double)newValue;
sizeview.setPrefWidth( w - 20 );
sizeview.layout();
}
});
return sizeview;
}
}

