Desktop Java

Swing and JavaFX: working with JFXPanel

I soon will have to deal with JavaFX in a Swing based fat client – oh sorry, of course I meant “multi-layered rich-client”!

So this brings me to have a look at the JFXPanel. The JFXPanel is a javax.swing.JComponent to embed JavaFX content into Swing-UIs. A JFXPanel can be used similar to a JPanel and can be accessed via the EDT as a generic Swing component except dealing with JavaFX component must be done via the JavaFX application thread.

To play around with that stuff I created two similar Panels (Swing + JavaFX) each with a Button, a TextField and a Label and placed them in a JSplitPane and a JFrame:

A closer look inside

To try Swing <-> JavaFX interoperability the buttons actions are setting the text from TextField to JLabel and vice versa.
There is nothing special with the JPanel as its handles generic Swing stuff, but with the JFXPanel containing the JavaFX controls:

public class SwingFXPanel extends JFXPanel {

    private Button testButton;
    private TextField testTextField;
    private Label testLabel;
    private VBox pane;

    public SwingFXPanel() {
        init();
    }

    private void init() {
        testButton = new Button("I am a JavaFX Button");
        testTextField = new TextField();
        testLabel = new Label("empty");
        pane = new VBox();
        pane.setAlignment(Pos.CENTER);
        pane.getChildren().addAll(testTextField, testButton, testLabel);
        Platform.runLater(this::createScene);
    }

    private void createScene() {
        Scene scene = new Scene(pane);
        setScene(scene);
    }

    public Button getTestButton() {
        return testButton;
    }

    public TextField getTestTextField() {
        return testTextField;
    }

    public Label getTestLabel() {
        return testLabel;
    }
}

Important here: to add the Scene to the JFXPanel within the JavaFX Application Thread:

Platform.runLater(this::createScene);

If you call:

createScene()

from another thread you get a Runtime-Exception:

java.lang.IllegalStateException: Not on FX application thread; currentThread = AWT-EventQueue-0

Also each interaction with JavaFX related stuff must be placed on the JavaFX Application Thread:

E.g.:

Platform.runLater(() -> {
   swingFXPanel.getTestLabel().setText(swingPanel.getTestTextField().getText());
});
public class InteropFrame extends JFrame {

    private JSplitPane centralSplitPane;
    private SwingPanel swingPanel;
    private SwingFXPanel swingFXPanel;

    public InteropFrame(){
        init();
    }

    private void init() {
        setTitle("Swing <-> JavaFX Interoperatbiliy");
        setSize(500, 500);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());

        centralSplitPane = new JSplitPane();
        centralSplitPane.setDividerLocation(0.5);
        centralSplitPane.setResizeWeight(0.3);
        
        swingPanel = new SwingPanel();
        swingFXPanel = new SwingFXPanel();

        swingPanel.getTestButton().addActionListener((ActionEvent e) -> {
            Platform.runLater(() -> {
                swingFXPanel.getTestLabel().setText(swingPanel.getTestTextField().getText());
            });
        });


        swingFXPanel.getTestButton().setOnAction((javafx.event.ActionEvent t) -> {
            swingPanel.getTestLabel().setText(swingFXPanel.getTestTextField().getText());
        });

        centralSplitPane.setLeftComponent(swingPanel);
        centralSplitPane.setRightComponent(swingFXPanel);

        add(centralSplitPane, BorderLayout.CENTER);
    }
}

Alternatively also dealing with FXML is quite simple:

public class SwingFXMLPanel extends JFXPanel {

    @FXML
    private Button testButton;
    @FXML
    private TextField testTextField;
    @FXML
    private Label testLabel;

    private VBox rootPane;

    private URL fxmlResource;
    
    public SwingFXMLPanel(URL fxmlResource){
        this.fxmlResource = fxmlResource;
        init();
    }

    private void init(){
        rootPane = new VBox();
        FXMLLoader loader = new FXMLLoader(fxmlResource);
        loader.setController(this);
        loader.setRoot(rootPane);
        try {
            loader.load();
        } catch (IOException ex) {
            Logger.getLogger(SwingFXMLPanel.class.getName()).log(Level.SEVERE, null, ex);
        }

        testButton.setText("I am a JavaFX Button");
        testLabel.setText("empty");

        Platform.runLater(this::createScene);
    }

    private void createScene() {
        Scene scene = new Scene(rootPane);
        setScene(scene);
    }

    public Button getTestButton() {
        return testButton;
    }

    public TextField getTestTextField() {
        return testTextField;
    }

    public Label getTestLabel() {
        return testLabel;
    }

}

For me its very important to get the most possible acceptance of my co-workers to use JavaFX in Swing.

Therefore I want to simplify the specific FX application thread handling. So maybe this can be achieved if the major difference to using a JPanel is just to add:

private void createScene() {
        Scene scene = new Scene(rootPane);
        setScene(scene);
    }

and to call:

Platform.runLater(this::createScene);

inside the JFXPanel.

  • You can find the complete example code here.
Reference: Swing and JavaFX: working with JFXPanel from our JCG partner Jens Deters at the JavaFX Delight blog.

Jens Deters

Jens Deters is a Senior Software Developer working in the domain of Aviation Authorities. His main objectives are RIA and Desktop Applications also he loves to play with IoT related stuff.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

1 Comment
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Paula Vieira
Paula Vieira
5 years ago

Thank you for this nice example. I have downloaded the complete example but I am having some problems reproducing your video. Basically, whenever I press the swing button, the content of the Swing Text Field appears in both Swing and JavaFX Labels, and whenever I press the JavaFx button, the content of the Swing Text Field also appears in both Swing and JavaFX Labels. Do you have any idea what is happening?

Thanks in advance.

Back to top button