Sunday, August 31, 2008

Compiled JavaFX in Swing applications - again

Update: This doesn't work in the full release of JavaFX. Please see here for how to do this in Java FX 1.0.

I posted a while ago about problems I was having integrating a compiled JavaFX widget into a Swing application - theoretically it should have been possible, as it's all Java and Swing, but I hit a brick wall that looked like it was due to JavaFX being in early, early alpha/beta stage, so I left it and decided to come back to it when JavaFX was more stable.

Well, the preview SDK for JavaFX is now available, and I had a wet Sunday afternoon to spare, so I tried it again. Of course, as it has been 4 or 5 weeks since I last looked at JavaFX they have completely changed the structure and location of the classes, so it took me a while to get the hang of things again, but once I had done that actually getting a JavaFX widget into a Swing application was trivial really.

The screenshot above shows the Stopwatch example from the JavaFX SDK inside a Swing window - the two controls to the left are Swing controls.

The key to getting JavaFX to operate with Swing is the javafx.ext.swing.Canvas object, which has a getJComponent() method, so all you need to do is place all of your JavaFX content onto a Canvas object, then use the JComponent returned by Canvas.getJComponent() to add the JavaFX widget to any Swing container.

For the example above, the Stopwatch widget example runs in a JavaFX frame with no Canvas, but all we need to do is write a simple JavaFX Canvas object to wrap the Stopwatch, using the code below:

package stopwatch;

import javafx.ext.swing.Canvas;
import javax.swing.JComponent;

public class SWCanvas {
public function getChildComponent() {
var sw = StopwatchWidget{}
var c2:Canvas = Canvas {
content: sw
width: 400
height: 400
visible: true

return c2.getJComponent();

Once we have compiled that, we can use our JavaFX 'SWCanvas' object in our Swing application just like any other Java class, so the simple JFrame example pictured above does just that:

package stopwatch;

import javax.swing.*;
import java.awt.Dimension;
import java.awt.FlowLayout;

public class SwingStopwatch {
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
new SwingStopwatch().runMe();


public void runMe() {
JFrame jf = new JFrame("Swing and JavaFX Test");
jf.setPreferredSize(new Dimension(600,400));
jf.getContentPane().setLayout(new FlowLayout());

jf.getContentPane().add(new JButton("Click me"));
jf.getContentPane().add(new JTextField("Type into me"));

// JavaFX widget here!
stopwatch.SWCanvas c1 = new stopwatch.SWCanvas();


And that's all there is to it. The trickiest part of the whole thing was getting the classpath for the JavaFX runtime right when running the Swing application (your CLASSPATH should look something like: %JAVAFX_HOME%\lib\javafxgui.jar;%JAVAFX_HOME%\lib\javafxrt.jar;%JAVAFX_HOME%\lib\javafx-swing.jar;%JAVAFX_HOME%\lib\Scenario.jar).

With JavaFX's ability to create impressive animated user interfaces quickly, and with relatively little code, then the ability to add JavaFX widgets to any Swing container could be a great way to improve Swing applications incrementally, rather than rewriting the whole application in JavaFX.

I'm definitely not a JavaFX expert though, so if you want some more ideas of the very impressive things that you can do with JavaFX then check out James Weaver's JavaFX Blog.

1 comment:

wyz said...

Hi, I am exploring JavaFX too and I have problem interacting with JavaFX objects inside Java code.

I tried your example, but

stopwatch.SWCanvas c1 = new stopwatch.SWCanvas();

will not compile. And I did not read documentation indicating that we can use it like this (yet?).

Have you tried the example? Thanks.