X

How to Use JavaFX in Your Swing Application

JavaFX has been out for a couple of weeks now and the response has been great. Clearly there are bugs to work on and features to add, but the core is powerful and ready to go. One feature I've gotten lots of requests for is the ability to mix Swing with JavaFX code and graphics. The JavaFX samples page has an example of how to embed an existing Swing component or panel in your JavaFX application. However, we don't have an example of how to do the reverse: embed a JavaFX scene in your Swing application. I asked Rich and Jasper about this and they had the same need as well. After a long train ride to Devoxx this is the code they came up with.

First things first: What I'm about to show you is a hack. It is completely unsupported, uses private APIs, and almost certainly will break in the next version. However, if we see a lot of interest we may add a permanent supported API in a future release of JavaFX, so let us know what you think of it. Second, this is a hack. Most likely not all features of JavaFX will work in this scenario and we haven't done any more than trivial testing. Don't go building a production application using this technique. All that said, on with the show:

The JXScene class

The core of this hack is the JXScene class, a Swing component which knows how to draw JavaFX scene graph nodes. It uses reflection to instantiate your JavaFX scene class and then pull out the internal canvas object.

org.jdeskto.swingx.JXScene.java

package org.jdesktop.swingx;  import com.sun.javafx.runtime.Entry; import com.sun.javafx.runtime.TypeInfo; import com.sun.javafx.runtime.sequence.Sequence; import com.sun.javafx.runtime.sequence.Sequences; import java.awt.BorderLayout; import java.lang.reflect.Method; import javax.swing.JComponent; import javafx.reflect.*;  /**  * @author Richard Bair and Jasper Potts  */ public class JXScene extends JComponent {     private String script;     private Object scriptObject;      // fully qualified path to script that creates a Scene     public void setScript(String path) {         this.script = path;         try {             Class app = Class.forName(path);             final Method main = app.getMethod(Entry.entryMethodName(), Sequence.class);             Object o = main.invoke(null, Sequences.make(TypeInfo.String, new String[0]));                          System.out.println(o);              String helperName = "com.sun.javafx.scene.JSGPanelSceneImpl";             FXClassType type = FXContext.getInstance().findClass(helperName);             FXObjectValue panelSceneImpl = type.allocate();             panelSceneImpl.initVar("scene", FXLocal.getContext().mirrorOf(o));             panelSceneImpl.initialize();              FXValue jsgPanelV = type.getVariable("jsgPanel").getValue(panelSceneImpl);             JComponent jsgPanel = (JComponent)((FXLocal.ObjectValue) jsgPanelV).asObject();             add(jsgPanel, BorderLayout.CENTER);             scriptObject = o;         } catch (Exception e) {             e.printStackTrace();         }     }      public Object getScriptObject() {         return scriptObject;     }     public String getScript() {         return script;     }     public JXScene() {         setLayout(new BorderLayout());     } } 

Setting up a Swing / JavaFX application

With the JXScene class in your arsenal, here's the steps to use it:

  • download the jar of JXScene or compile it on your own from the code above
  • Create a project in NetBeans for the JavaFX part of your application. Put your JavaFX Script code in there.
  • Make your main JavaFX class extend Scene like this:
    public class MyScene extends Scene {     init {         content = [             Rectangle { width: 200 height: 200 fill: Color.BLUE },             Text { x: 20 y: 20 content: "Greetings Earthling!" fill: Color.WHITE }         ]     } } 
  • Create a Java or Desktop Java application in NetBeans for the Java / Swing part of your application. Put your Swing code in there.
  • Add this code to your Swing app:
    JXScene scene = new JXScene(); // create a new JXScene scene.setScript("com.sun.test.MyScene"); // the name of your main JavaFX class mainPanel.add(scene); // add the scene your swing scene 
  • In your Java project go to the Project Properties dialog and the Libraries tab.
  • Add the JXScene project or jar to the classpath of the Java project
  • Add the JavaFX project to the classpath of the Java project
  • Download the JavaFX standalone (commandline) SDK from here.
  • Add all of the jars in the lib/desktop and lib/shared directories of the JavaFX SDK to the classpath of the Java project
  • Compile your JavaFX project
  • Compile and run your Java project

If everything went well you will get a screen that looks like this:
testjxscene.TestJXSceneAppScreenSnapz001.png

The menu and frame and bottom status bar are from my Swing application, but the center panel is my JavaFX scene.

Deployment

That's all there is to it for working locally, but what about when you want to deploy your application? The JavaFX runtime is always deployed as a shared cached JNLP extension, so to use it from your own deployed webstart application just add the extension to the resources section of your JNLP.

in TestJXScene.jnlp

<extension name="JavaFX Runtime" href="http://dl.javafx.com/javafx-rt.jnlp" />

Interaction between JavaFX and Java

For any non-trivial application you are going to want to have some interaction between the Java and JavaFX sides of your application. In general you should try to call Java code from the JavaFX side, not the other way around. While it is possible to invoke the various JavaFX methods using reflection, this is cumbersome and not recommended. Instead, make your JavaFX code implement a Java interface. Then the Java side can cast your JavaFX objects to that interface and invoke your functions as if they were Java methods. For example, the JavaFX scene above could be declared to extend the java.awt.event.ActionListener like this:

in com.sun.test.MyScene.fx

public class MyScene extends Scene, ActionListener {     override public function actionPerformed(e:ActionEvent)  {         println("this is the callback");     }      init {  // more code 

Now the Java code can cast the JavaFX object to an ActionListener and invoke the actionPerformed method like this:

JXScene scene = new JXScene(); scene.setScript("com.sun.test.MyScene"); mainPanel.add(scene); ActionListener al = (ActionListener) scene.getScriptObject(); al.actionPerformed(null); 

Conclusion

Swing and JavaFX interaction is clearly possible but not as clean as we would like. If you have any suggestions for what you'd like to do with Swing/JavaFX mixing and what the ideal API would be, please let us know. We always appreciate your feedback.

Be the first to comment

Comments ( 0 )
Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.