Jump into JavaFX, Part 3: The basic APIs

Get started with javafx.lang, javafx.util, and javafx.animation

The JavaFX APIs cover a lot of distance, and so will the third and fourth installments in the Jump into JavaFX series. Jeff eases you into the JavaFX APIs with a tour of the javafx.lang and javafx.util packages. He then pops open javafx.animation to demonstrate JavaFX's support for keyframe animation. Finally, you'll use the JavaFX API classes found in javafx.application and javafx.scene to create an application window, paint its stage, and populate the stage with text, shapes, and images. Examples in this article are based on JavaFX Preview SDK.

When working in JavaFX, you have access to both the standard Java APIs and the JavaFX APIs, which were created specifically for rich Internet application development. The JavaFX APIs are organized into the following packages:

  • javafx.animation
  • javafx.application
  • javafx.ext.swing
  • javafx.input
  • javafx.lang
  • javafx.scene
  • javafx.scene.effect
  • javafx.scene.effect.light
  • javafx.scene.geometry
  • javafx.scene.image
  • javafx.scene.layout
  • javafx.scene.media
  • javafx.scene.paint
  • javafx.scene.text
  • javafx.scene.transform
  • javafx.util

In this third article in the Jump into JavaFX series we'll begin to explore many the API classes located in these packages. I'll first look at some of the basic API classes found in javafx.lang and javafx.util. I'll then introduce you to JavaFX's support for keyframe animation, via classes in the javafx.animation package, and to the essential classes found in the javafx.application package. We'll use these to create a sample JavaFX application's decorated top-level window and specify its staging area. Finally, we'll paint the application's stage and specify its scene using node-based text, geometric shapes, and images. Don't worry if some of the concepts discussed here (stage and scene, for instance) are unfamiliar; they won't be after you've done the exercises!

JavaFX Preview SDK

The discussion in this article is based on the JavaFX APIs found in the JavaFX Preview SDK, which will differ somewhat from the APIs found in JavaFX SDK 1.0. What you learn here will serve as background for Jeff's first look at JavaFX SDK 1.0, in the final two articles of this series.

Basic APIs

JavaFX provides several basic APIs via the javafx.lang and javafx.util packages. The javafx.lang package specifies the language-oriented DeferredTask, Duration, and Sequences classes. The javafx.util package specifies the single StringLocalizer class. Except for DeferredTask, we'll explore all of these classes in this section.

In order to explore these classes, you'll need to start up your NetBeans IDE and use its New Project wizard to introduce a new APIDemo1 project with apidemo1.BasicAPIs as the main file. As we explore the basic APIs, we'll insert a series of scripts into BasicAPIs.fx, replacing the previous script with a new one.

Duration

The first class we'll explore is Duration. To begin, replace the skeletal BasicAPIs.fx's // place your code here line with the following script:

Listing 1. Introducing the Duration class

import java.lang.System;

import javafx.lang.Duration;

var time1: Duration;
System.out.println (time1); // Output: 0.0ms

var time2 = 10m;
System.out.println (time2) // Output: 600000.0ms

This script accesses javafx.lang's Duration class, which represents a time interval. When a Duration variable is declared, it is by default initialized to 0.0 milliseconds. Alternately, you may specify an initializer based on a time literal, which is an integer suffixed with m (minutes), s (seconds), h (hours), or ms (milliseconds).

Client-side Java's evolutionary leap

Where does JavaFX fit in the big picture of Sun's client-side offerings? Find out what client-side Java's leaders and innovators have to say about it: "Client-side Java's evolutionary leap" features insight from Richard Bair, Tim Boudreau, Stephen Chin, Danny Coward, Joseph Darcy, Mikael Grev, Kirill Grouchnikov, Cay Horstmann, and Jim Weaver.

Just as Java does for the String class, JavaFX Script provides syntactic sugar for Duration. Along with time literals, which represent Duration instances, this sugar includes addition (+) and subtraction (-) operators for adding/subtracting duration values, and multiplication (*) and division (/) operators for multiplying/dividing a duration value by an arbitrary number. You can see these operators in Listing 2.

Listing 2. Add, subtract, multiply, and divide time literals via the addition, subtraction, multiplication, and division operators

java.lang.System.out.println (10m+20s); // Output: 620000.0ms
java.lang.System.out.println (10m-20s); // Output: 580000.0ms
java.lang.System.out.println (1h/4); // Output: 900000.0ms
java.lang.System.out.println (1h*2.5) // Output: 9000000.0ms

Additionally, you can compare time literals for equality (30s == 30s), inequality (30s != 40s), and more (10m < 20m, 10m <= 10m, 10m > 20m, 10m >= 10m). You can also negate time literals (-2h), and invoke various conversion functions (3h.toMinutes() and 23m.toSeconds ()).

When the JavaFX compiler encounters an expression consisting of time literals and operators, it converts this syntactic sugar to equivalent Duration instances and function calls. For example, var startTime = 10m is equivalent to var startTime = Duration { millis: 600000 }, and startTime+20s is equivalent to startTime.add (Duration { millis: 20000 }).

A bug to watch out for

The NetBeans implementation of JavaFX Script is somewhat buggy. For example, var startTime = 10m; startTime += 10s; compiles correctly, but results in a thrown java.lang.ClassCastException at runtime. The workaround to this bug involves replacing += with +, which results in startTime = startTime+10s;. (This bug has been reported. See the Resources section to learn about reporting bugs in JavaFX.)

Sequences

The javafx.lang package's Sequences class is useful for manipulating sequences via its various utility functions. For example, you can sort a sequence via the Sequences public static sort(seq: java.lang.Object[], c: java.util.Comparator): <any>[] function, which is demonstrated by the following script:

Listing 3. Sorting a sequence

var ages = [32, 23, 45, 19, 67, 98, 52];
java.lang.System.out.println (javafx.lang.Sequences.sort (ages, null))
// Output: [ 19, 23, 32, 45, 52, 67, 98 ]

Additional interesting functions that you'll find in Sequences include:

  • public static binarySearch(seq: java.lang.Comparable[], key: java.lang.Comparable): Integer -- search the specified sequence for the specified object using the binary search algorithm.
  • public static reverse(seq: java.lang.Object[]): <any>[] -- reverse a sequence.
  • public static shuffle(seq: java.lang.Object[]): <any>[] -- randomly permute a sequence (as in shuffling a deck of cards) using a default source of randomness.

StringLocalizer

To localize string literals in JavaFX Script, you could take advantage of Java's localization support. However, this support doesn't let you express localization in a declarative manner. Fortunately, you can avoid working with Java's localization support by taking advantage of the javafx.util package's StringLocalizer class.

StringLocalizer provides four attributes, starting with propertiesName, which identifies the package and base names of a properties-based resource bundle file. The base name has the form basename_xx.fxproperties, where xx is a two-letter locale designation (en for English); the file's entries have the form "key"="value" (the double quotes are necessary).

The key attribute identifies one of the keyed entries, and is assigned a string that doesn't contain embedded double quotes. The locale attribute identifies the java.util.Locale object that's used to supply the xx portion of the base name. Finally, the defaultString attribute identifies a default string to return if no appropriate localized string is found in the resource bundle.

Localization is performed by invoking the public bound localizedString(): java.lang.String function. (This is just one of five functions located in StringLocalizer.) The following script demonstrates localizedString(), along with the propertiesName, key, and locale attributes:

Listing 4. Declarative localization in JavaFX

var localizer = javafx.util.StringLocalizer 
                { 
                    key: "Welcome" 
                    propertiesName: "apidemo1.foo.bar.MyResources"                    
                }
java.lang.System.out.println (localizer.localizedString ());
localizer.locale = java.util.Locale.FRENCH;
java.lang.System.out.println (localizer.localizedString ())

This script assumes that APIDemo1 contains a build\classes\apidemo1\foo\bar directory hierarchy, and that bar contains MyResources_en.fxproperties (my default locale is English) and MyResources_fr.fxproperties properties files. foo\bar and suitable same-named properties files are located in this article's code archive.

Now that you're familiar with the basic APIs, let's explore JavaFX's API support for animation.

Keyframe animation

Because animation is crucial to the success of rich internet applications, JavaFX supports keyframe animation, a declarative animation model based on timelines (time sequences on which animations play out) and key frames (snapshots of animation state at points in time relative to their containing timelines). This support mostly consists of classes in the javafx.animation package.

A timeline is expressed as a Timeline instance, which provides descriptive attributes and functions that control the timeline's playback. Attributes include autoReverse, which declares that each animation cycle plays in reverse to the previous cycle, repeatCount, which declares a number of animation cycles, and so on. Functions are start(), stop(), pause(), and resume().

A key frame is expressed as a KeyFrame instance. This class provides a list of key values (as a sequence of KeyValue objects). Each key value describes an end-state of a property at the time of the key frame, together with a function used to calculate "in-between" values relative to the previous key frame for the property.

The KeyFrame class also provides a list of sub-timelines (as a sequence of Timelines) and a trigger. Each sub-timeline is evaluated with its starting point relative to the key frame's time attribute value. A trigger is a void function that's executed at the time instant specified by the key frame.

JavaFX Script supports two kinds of keyframe animation: discrete and interpolated. I'll discuss both in the next sections.

Discrete keyframe animation

With discrete keyframe animation, the value of a given property instantaneously transitions to the value given in the key frame at the time instant of the key frame. In effect, this would be like replacing the previous image with the next one in a sequence of images.

To demonstrate discrete keyframe animation for yourself, start NetBeans and use its New Project wizard to introduce a new APIDemo2 project with apidemo2.Animation as the main file. Replace the skeletal Animation.fx's // place your code here line with the following script:

Listing 5. Discrete keyframe animation demo

var timeline = javafx.animation.Timeline 
{
     autoReverse: true // reverse direction on each cycle
     repeatCount: javafx.animation.Timeline.INDEFINITE // never stop 
     keyFrames: for (i in [30..40]) 
     {
          javafx.animation.KeyFrame 
          {
               time: 100ms*indexof i // each frame is 100ms apart
               action: function ()
               {
                   java.lang.System.out.println (i)
               }
          }
     }
}
timeline.start ()

The script in Listing 5 creates a Timeline instance and starts the animation via this instance. This animation continues indefinitely; each cycle increases property i from 30 through 40, or decreases this property from 40 through 30, in an increase/decrease pattern. We can understand this animation in terms of its various attributes:

  • autoReverse (of type Boolean) reverses the direction on each animation cycle when set to true.
  • repeatCount (of type Number) sets the number of animation cycles; INDEFINITE means until Timeline's stop() function is called.
  • keyFrames (of type KeyFrame[]) describes 10 animation key frames via a KeyFrame sequence:
    • time (of type Duration) defines the reference elapsed time offset within a single cycle of the Timeline instance at which the associated properties will be set, and at which the trigger will be executed. The first time offset is 0 milliseconds, and each subsequent offset is 100 milliseconds later than its predecessor.
    • action (of type function():Void) identifies a trigger block (expressed as a function) that's called when the elapsed time on a cycle passes the specified time of this KeyFrame. The function is called if the elapsed time passes the indicated value, even if it was never exactly equal to the time value.

Interpolated keyframe animation

Unlike discrete keyframe animation, interpolated keyframe animation calculates key frames that lie between a few chosen key frames, which are specified via at-based blocks. Each block informs JavaFX Script on how to calculate intermediate key frames via => and tween sugar, and interpolators defined by the Interpolator class. Try this for yourself by replacing the previous script in your NetBeans editor with the following one:

Listing 6. Interpolated keyframe animation demo

1 2 3 4 Page 1
Page 1 of 4