RIA Development Center
Features Tips Events
Delivering business processing logic through RIA (Rich Internet Application) format is very attractive to architects, developers and operators of IT shops. RIA combines the richness of the desktop application with the ease of deployment, ubiquity and platform independence of the web application. RIAs in the form of mashups, are very popular in the consumer space but have yet to see similar success in the enterprise space. Enterprise applications are stateful and process-centric in nature. Read more
See more tips
Get regular email alerts when we publish new features!
DevX RIA Development Update

More Newsletters
Crash Course in Next-Gen RIA: AIR, Silverlight, and JavaFX (cont'd)
More Resources
  • Getting Started with Microsoft Silverlight
  • QuickStarts: Silverlight Development Using
        the .NET Framework
  • JavaFX Stopwatch
    JavaFX Script is a scripting language intended to simplify the creation of rich UIs for Java clients. It's implemented in Java and uses Java APIs for 2D and 3D graphics as well as UI controls. JavaFX Script supports a declarative syntax for UI definition, somewhat similar to the ones used by Microsoft in XAML and Adobe in MXML. However, it's not XML-based. In fact, it's a real programming language—not just a markup tool—so you can write an entire application in JavaFX Script.

    Setting Up Development Environment
    Follow these steps to set up your development environment for JavaFX Script:

    1. Download and install JDK 6u1 with NetBeans 5.5.1.
    2. Install the JavaFX plug-in from NetBeans packages manager.

    JavaFX Script has no special project type, so to start you just create a regular Java project using File->New Project… from the menu (see Figure 7).

    Click to enlarge
    Figure 7. New Project

    The new project will be empty. Enter "stopwatch" as the project name, uncheck "Create Main Class", and click "Finish" (Figure 8 shows the result).

    Click to enlarge
    Figure 8. Project Name

    Next, right-click on <default package> and select New->File/Folder… (see Figure 9 for the result). The next time you add a file, the "JavaFX File" option should appear in the pop-up menu under New.

    Click to enlarge
    Figure 9. Adding JavaFX File

    Now you have an empty JavaFX project. To enable your IDE to run it, right-click on the project name and chose "Properties". Select "Other" from the categories list, pick "JavaFX File" as the file type, and click "Next". Then enter "stopwatch" as the file name and click Finish (Figure 10 shows the result).

    Click to enlarge
    Figure 10. Adding JavaFX File Continued

    To set the main JavaFX file in Project Properties, enter "stopWatch" into the "Arguments" field on the "Run" property sheet and click OK (see Figure 11 for the results).

    Click to enlarge
    Figure 11. Setting Main JavaFX File in Project Properties

    Now paste the following minimal application code into the stopWatch.fx file:

    
    import javafx.ui.*;
    import javafx.ui.canvas.*;
    
    Frame {
        title: "StopWatch"
    	width: 260,	height: 280
    	content:  []
    	visible: true
    }
    

    Frame{} declares the application frame. You separate its attributes with either new lines or commas.

    Press F6 to run the application (see Figure 12 for the window you'll render).

    Click to enlarge
    Figure 12. Minimal JavaFX Application

    Building a UI from Original Artwork
    First download the accompanying JavaFX Script code and extract it into the "assets" subfolder within your "src" folder. Next, make it display the stopwatch background and buttons by adding the following code inside the square brackets of Frame's content attribute:

    
    Canvas {
        background: white
        content: 
            [
                // Background
                ImageView {
                    transform: [Translate {x: 5, y: 5} ]
                    image: Image { url: "assets/sport_bg.png"  }
                },
                // Start / Stop button
                ImageView {
                    transform: [Translate {x: 197, y: 16.85} ] 
                    image: Image { url: "assets/start_btn.png"  }
                },
                // Clear button
                ImageView {
                    transform: [Translate {x: 190.5, y: 193.35}] 
                    image: Image { url: "assets/clear_btn.png"  }
                },
            ]
    }
    

    You use the Canvas object to create a canvas on which you can render graphics. ImageView displays bitmap images (note how the transform attribute is used to position them).

    Run the application to verify that it displays the background and buttons. Unfortunately Java and JavaFX Script offer no easy, cross-platform way to support transparent windows. So you can't easily get the nice custom-shaped window effect you did with AIR. Also, you still have to convert a hand and the background artwork into a vector format that JavaFX Script recognizes. You'll find a very good SVG-to-JavaFX converter online (naturally implemented in Java).

    Working with the converter is easy. Just open the SVG file and the converter turns it into JavaFX Script source, renders it on the screen, and allows you to save the resulting code. I found only one bug: it doesn't convert the corner radius values used in SVG into the arcWidth/arcHeight attributes used in JavaFX Script, so you have to manually multiply all such values by a factor of two. See Figure 13 for a preview of the Stopwatch SVG artwork displayed within the converter. (Note that corner radiuses are smaller than they should be.)

    Click to enlarge
    Figure 13. SVG-to-JavaFX Converter

    Converters normally create wrapper classes, which contain functions to access all nodes in the source SVG individually. For this simple demo you can just cut the necessary code from the wrapper class and insert in into your markup.

    Next, add the items in Listing 7 to Canvas's content attribute in order to render the digital display and hands.

    Note that the arcWidth and arcHeight attributes contain 2* multipliers. That's a small example of JavaFX Script's power. You can mix the code with markup at will (because markup is code too!). So in this example drawHand() is a function, which is defined in Listing 8. This code was copied from the output of the SVG-to-JavaFX converter and wrapped into the drawHand() function. You can efficiently reuse JavaFX UI elements this way.

    These pieces combined give you the complete user interface for the stopwatch application. Press F6 to test it before going further.

    Implementing the Animation
    JavaFX Script offers an interesting approach for implementing dynamic UI behaviors. It uses the bind operator, which allows you to associate a variable or attribute with a certain expression so that every time the expression changes, the attribute changes too. Add the following code right below the original import operators and above the Frame object:

    
    import java.lang.System;
    import java.lang.Math;
    
    class stopWatch {
        attribute _is_running: Boolean;
        attribute _startTime: Integer;
        attribute time: Integer;
        attribute timeString: String;
        attribute angleMinutes: Number;
        attribute angleSeconds: Number;
        
        attribute timer: Number;
        operation updateTime();
        operation start();
        operation stop();
        operation clear();
    }
    
    attribute stopWatch.angleSeconds = 0.0;
    attribute stopWatch.angleMinutes = 0.0;
    attribute stopWatch.timeString = "00:00:00.00";
    attribute stopWatch._startTime = System.currentTimeMillis();
    attribute stopWatch.time = 0;
    attribute stopWatch._is_running = false;
    
    var watch = new stopWatch();
    

    With this code you declared the stopWatch class, assigned initial values to the attributes, and created an instance of the class. You can add operations to a class definition without implementing them.

    Now you can bind some markup attributes to your class attributes as follows:

    1. Change the content of the Text element from "00:00:00.00" to bind watch.timeString.
    2. Add the rotate transformations to the second and minute hands, and bind the rotation angles to watch.angleSeconds and watch.angleMinutes, respectively:
      
      // Seconds hand
      Group {
          transform: [Translate{x: 122.38, y: 122.38}
                      Rotate{angle: bind watch.angleSeconds}]
          content: [ drawHand()  ]
      },
      // Minutes hand
      Group {
          transform: [Translate{x: 122.38, y: 75.65}, Scale{x: 0.286, y: 0.286}
                      Rotate{angle: bind watch.angleMinutes}]
          content: [ drawHand()  ]
      },
      

      Square brackets denote a series of objects assigned to the attribute. In this case you applied several transformations simultaneously.

    Try altering the initial values and then running the application to see that the UI changes as you modify class attributes.

    Now you need a timer. The timer implementation involves creating the attribute (which changes its value periodically using the dur operator), adding a trigger (which fires when that attribute changes), and implementing the handler code within that trigger. To that end, add the following code below the last attribute initialization line:

    
    stopWatch.timer = bind [1..50] dur 1000 linear
    while _is_running
    continue if true;
    
    trigger on stopWatch.timer = value {
        if (_is_running) {
            time = System.currentTimeMillis() - _startTime;
            updateTime();
        }
    }
    
    operation stopWatch.updateTime() {
        var hours: Integer = Math.floor(time / 3600000);
        var rem: Integer = time % 3600000;
        var minutes: Integer = Math.floor(rem / 60000);
        rem %= 60000;
        var seconds: Integer = Math.floor(rem / 1000);
        var milliseconds: Integer = rem % 1000;
    
        angleMinutes = minutes * 30 + seconds * 0.5;
        angleSeconds = seconds * 6 + milliseconds / 1000 * 6;
    
        timeString = "{hours format as <<00>>}:{minutes format as <<00>>}:{seconds 
    format as <<00>>}.{milliseconds/10 format as <<00>>}"; }

    Your timer triggers display updates 50 times per second. The updateTime() operation uses JavaFX Script's format as operator to format digital display output. The operator supports formatting using conventions of the Java classes java.text.DecimalFormat, java.text.SimpleDateFormat, and java.util.Formatter. Curly brackets allow you to include the code within literal strings. Although a bit unusual, the syntax is very efficient.

    Change the initial value of the _is_running attribute to true and test the application to verify that the timer is working.

    Adding the Interactivity
    First add the implementations for the start(), stop(), and clear() operations:

    
    operation stopWatch.start() {
        _is_running = not _is_running;
        if (_is_running) {
            _startTime = System.currentTimeMillis() - time;
        } else {
            time = System.currentTimeMillis() - _startTime;
        }
    }
    operation stopWatch.stop() {
        _is_running = false;
        clear();
    }
    operation stopWatch.clear() {
        _startTime = System.currentTimeMillis();
        time = 0;
        updateTime();
    }  
    

    Note that the logical not operator uses not notation, which is different from Java and C++, where you use an exclamation point.

    Next, add the appropriate event handlers to the Start/Stop and Clear buttons:

    
    // Start / Stop button
    ImageView {
        transform: [Translate {x: 197, y: 16.85} ] 
        image: Image { url: "assets/start_btn.png"  }
        onMousePressed: operation(event) {
            watch.start();
        }
    },
    // Clear button
    ImageView {
        transform: [Translate {x: 190.5, y: 193.35}] 
        image: Image { url: "assets/clear_btn.png"  }
        onMousePressed: operation(event) {
            watch.clear();
        }
    },
    

    To set an element's event handler you assign the necessary operation to the appropriate attribute of the element. Mouse event handlers must have one argument, so you wrap the stopWatch class methods inside an in-place operation definition.

    Finally, add the following attribute to the Frame element, which directs the application to call the onClose handler when the window is closed:

    
    onClose: operation() {
            watch.stop();
            delete watch;
        }
    

    This assures the application shuts down correctly and prevents it from hanging on exit. Otherwise when a user closes the application while it's running, the GUI thread will exit but the timer thread could hang and prevent the runtime from unloading correctly.

    Deployment
    Building a JavaFX project with NetBeans produces a stopwatch.jar package, which you can deploy with Java Web Start. The process involves the following steps:

    1. Create a JNLP file with an application description.
    2. Sign the .jar files (when deploying your JavaFX application, you have to sign javafxrt.jar and swing-layout.jar).
    3. Upload the .jar and .jnlp files to your web server.
    4. Configure your web server to recognize the .jnlp extension:
      
      AddType application/x-java-jnlp-file JNLP
      

    5. Publish the link to the .jnlp file you uploaded.

    (The JFX Wiki has a step-by-step guide for deploying JavaFX applications using Java Web Start.)

    Click here for a live example of the JavaFX Stopwatch demo deployment.

    Bringing Richness to Java UIs
    In a sense, Java is the oldest internet applications platform of the three; it's just been missing the "rich" part. JavaFX Script is a big leap in that direction:

    • Its declarative syntax for UI markup is done nicely, and its incremental evaluation is convenient for describing complex UIs concisely.
    • The SVG-to-JavaFX conversion tool allows efficient import of artwork in the SVG format, which Adobe Illustrator supports. This enables you to split projects between designers and developers—a critical feature of any competitive RIA platform.
    • Also notable are the ability to access the power of Java APIs and the compatibility for real cross-platform deployment.

    The bad news is it's slow. Classical Java often feels slow but JavaFX Script is even slower. This might spoil all its potential advantages, especially when it comes to highly dynamic, rich user interfaces.

    Performance aside, however, JavaFX Script seems to be well positioned against Adobe AIR but it loses out in the ease-of-deployment and convenience departments. JavaFX Script is more hardcore—it offers the experienced programmer more sugar, but if you just want quick results AIR probably is a better choice.

    Previous Page: AIR Stopwatch Next Page: Distinct Approaches, Common Goals
    Page 1: IntroductionPage 4: JavaFX Stopwatch
    Page 2: The Silverlight StopwatchPage 5: Distinct Approaches, Common Goals
    Page 3: AIR Stopwatch 
    We have a winner in the RIA Run contest! Check out the Contest Winners Gallery and see which entries took the top prizes. You can play the games, too! Also, be sure to check out our interview with the grand prize winner to see how he crafted his winning entry. (Silverlight 2 Beta 2 required)