Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Crash Course in Next-Gen RIA: AIR, Silverlight, and JavaFX : Page 4

Get a hands-on introduction to the newest RIA technologies from Adobe, Microsoft, and Sun by building demo applications using AIR (Flex), Silverlight (CLR), and JavaFX.


advertisement
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.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap