Featured Post

Applying Email Validation to a JavaFX TextField Using Binding

This example uses the same controller as in a previous post but adds a use case to support email validation.  A Commons Validator object is ...

Wednesday, July 13, 2016

Calling a Swing Listener from JavaFX

This blog post demonstrates how to call a legacy Swing ActionListener from JavaFX.  A JButton ActionListener increments a variable and sets a JTextField to the incremented value.  A JavaFX Button is added to the form that calls the same ActionListener object used by the Swing code.


Before you start integrating JavaFX into your Swing app, read the following blog post for some recommendations.

The Swing / JavaFX Hybrid App

The JFrame's contentPane contains some Swing components, a JButton and a JTextField, and a JFXPanel.  The JFXPanel contains the JavaFX code which is a JavaFX Button placed in a Scene.  In JavaFX, a Scene is a root node (lowercase) of a JavaFX UI object graph.

Because we're dealing with two different event threads -- the AWT thread and the JavaFX thread -- we have to surround the Swing-manipulating code with SwingUtilities.invokeLater().  This is standard practice for long-running operations run in a Swing app off the AWT thread.  JavaFX needs the same treatment.  This isn't demonstrated in this post, but you should frame any JavaFX-updating code with Platform.runLater().


public class SwingApp extends JFrame {

    int counter = 0;

    public SwingApp() {

        JButton incrButton = new JButton("Swing Incr.");

        JTextField counterTextField = new JTextField( "0", 20 );

        ActionListener actionListener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                counter++;
                counterTextField.setText(String.valueOf(counter));
            }
        };

        incrButton.addActionListener(actionListener);

        Container container = getContentPane();
        container.setLayout(new BoxLayout(getContentPane(), BoxLayout.PAGE_AXIS));
        container.add( incrButton );
        container.add( counterTextField );

        // add FX components
        JFXPanel jfxPanel = new JFXPanel();

        Button fxIncrButton = new Button("FX Incr.");
        fxIncrButton.setOnAction( (evt) ->
            SwingUtilities.invokeLater(
                    () -> actionListener.actionPerformed(new ActionEvent(new Object(), 0, "command"))
            )
        );

        Scene scene = new Scene( fxIncrButton );
        jfxPanel.setScene( scene );

        getContentPane().add( jfxPanel );
    }

    public static void main(String[] args) {
        SwingApp app = new SwingApp();
        app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        app.setVisible(true);
        app.pack();
    }
}

Usually, you'll see an anonymous class used with addActionListener().  I factored the ActionListener out so that I could call it from my onAction handler in JavaFX.  After properly surrounding the Swing code with invokeLater(), I create an ActionEvent and call the actionPerformed() method.  Note that this assumes that the ActionEvent is not being used in the ActionListener.  If you're relying on ActionEvent to provide parameters, you might have to create a Swing component, assign an ID, or use the command String.


No comments:

Post a Comment