Now that the JavaFX 1.2 SDK is available, we'd like to give you a jump start by posting a large excerpt of Chapter 1 from the Pro JavaFX book (by Jim Weaver, Weiqi Gao, Stephen Chin and Dean Iverson). This Apress Early Access eBook is the first JavaFX book that includes the newly-introduced 1.2 features, and we are pleased to announce that it will be available on the Apress Pro JavaFX site in the next few days.
Note: If you purchase/download the Pro JavaFX Early Access eBook now (or have already done so), you will be notified when the JavaFX 1.2 updates are available, and will be able to download them at no additional charge.
Pro JavaFX will also be released as in a 500+ page printed book shortly. By the way, if you're attending any of our JavaFX sessions at JavaOne University or the JavaOne conference, your assignment is to download the JavaFX 1.2 SDK and begin getting up to speed by working through this excerpt (just kidding). Here ya go:
Developing Your First JavaFX Program: "Hello Earthrise"
On Christmas Eve in 1968 the crew of Apollo 8 entered lunar
orbit for the first time in history.
They were the first humans to witness an “Earthrise”, taking the
magnificent picture shown in the screenshot in Figure 1-4 below. This image, and the accompanying audio MIDI
file, is dynamically loaded from the Pro JavaFX book website when the program
starts, so you’ll need to be connected to the Internet to view/hear it:
Figure 1-4. The
Hello Earthrise Program
In addition to demonstrating how to dynamically load images
over the Internet, and play audio media, this example shows you how to use
animation in JavaFX. Now it’s time for
you to compile and run the program.
We’re going to show you two ways to do this: From the command-line, and
using NetBeans with the JavaFX plug-in.
Compiling and Running from the Command-Line
We usually use an IDE to build and run JavaFX programs, but
to take all of the mystery out of the process we’re going to use the
command-line tools first.
Note For
this exercise, as with most others in the book, you’ll need the source
code. If you’d prefer not to type the
source code into a text editor, you can obtain the source code for all of the
examples in this book from the code download site. See the Resources section at the end of this
chapter for the location of this site.
Assuming that you’ve downloaded and extracted the source
code for this book into a directory, follow the directions in this exercise,
performing all of the steps as instructed. We'll dissect the source code after
the exercise.
Compiling and Running the Hello Earthrise Program from
the Command-Line
You’ll use the javafxc and javafx command-line tools to compile and run the
program in this exercise. From the
command-line prompt on your machine:
1. Navigate to the Chapter01/Hello
directory.
2. Execute the following command to compile the HelloEarthRiseMain.fx
file. Note that the javafxc command-line tool for JavaFX is analogous to
the javac tool for Java
programs.
javafxc -d . HelloEarthRiseMain.fx
Because
the –d option was used in this command, the class
files generated are placed in directories matching the package
statements in the source files. The root
of those directories are specified by the argument given for the –d option, in this case the current directory. We’ll cover package
statements in a bit.
3. To run the program, execute the following
command. Note that the javafx command-line tool for JavaFX is analogous to the
java tool for Java programs. Note as well that we use the fully-qualified
name of the script that will be executed, which entails specifying the nodes of
the path name and the name of the script, all separated by periods. Unlike when compiling scripts, running and
appending the FX extension will result in an error.
javafx projavafx.helloearthrise.ui.HelloEarthRiseMain
The program should appear as shown in Figure 1-4
above, with the text scrolling slowly upward, reminiscent of the Star Wars
opening crawls.
Congratulations on completing your first exercise as you
explore JavaFX!
Understanding the Hello Earthrise Program
Now that you’ve run the application, let’s walk through the
program listing together. The code for the Hello Earthrise application is shown
in Listing 1-1.
Listing 1-1. The HelloEarthRiseMain.fx Program
/*
* HelloEarthRiseMain.fx - A JavaFX Script "Hello World" style example
*
* Developed 2009 by James L. Weaver jim.weaver [at] javafxpert.com
* as a JavaFX Script SDK 1.2 example for the Pro JavaFX book.
*/
package projavafx.helloearthrise.ui;
import javafx.animation.transition.TranslateTransition;
import javafx.animation.*;
import javafx.stage.Stage;
import javafx.scene.*;
import javafx.scene.image.*;
import javafx.scene.media.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.*;
var textRef:Text;
// Provides the animated scrolling behavior for the text
var transTransition = TranslateTransition {
duration: 75s
node: bind textRef
toY: -820
interpolator: Interpolator.LINEAR
repeatCount: Timeline.INDEFINITE
}
Stage {
title: "Hello Earthrise"
scene: Scene {
height: 387
width: 516
content: [
ImageView {
image: Image {
url: "http://projavafx.com/images/earthrise.jpg"
}
},
Group {
layoutX: 50
layoutY: 180
content: [
textRef = Text {
layoutY: 100
textOrigin: TextOrigin.TOP
textAlignment: TextAlignment.JUSTIFY
wrappingWidth: 380
// Note that this syntax creates one long string of text
content:
"Earthrise at Christmas: "
"[Forty] years ago this Christmas, a turbulent world "
"looked to the heavens for a unique view of our home "
"planet. This photo of Earthrise over the lunar horizon "
"was taken by the Apollo 8 crew in December 1968, showing "
"Earth for the first time as it appears from deep space. "
"Astronauts Frank Borman, Jim Lovell and William Anders "
"had become the first humans to leave Earth orbit, "
"entering lunar orbit on Christmas Eve. In a historic live "
"broadcast that night, the crew took turns reading from "
"the Book of Genesis, closing with a holiday wish from "
"Commander Borman: \"We close with good night, good luck, "
"a Merry Christmas, and God bless all of you -- all of "
"you on the good Earth.\""
//The approximate color used in the scrolling Star Wars intro
fill: Color.web("0xBBC36B")
font: Font.font("SansSerif", FontWeight.BOLD, 24);
}
]
clip:
Rectangle {
width: 430
height: 85
}
}
]
}
}
// Start playing an audio clip
MediaPlayer {
autoPlay: true
repeatCount: MediaPlayer.REPEAT_FOREVER
media: Media {
source: "http://projavafx.com/audio/zarathustra.mid"
}
}
// Start the text animation
transTransition.play();
Now that you’ve seen the code, let’s take a look at its constructs
and concepts in detail, beginning with one of the easiest – comments.
Comments
There are two types of comments in JavaFX: multiline
comments and single-line comments. Multiline comments begin with
the two characters /*
and end with the same two characters in reverse order */. JavaFX will ignore anything in
between. The beginning of the listing shows an example of a multiline comment.
Single-line comments begin with the two characters //. Anything that follows these two
characters on a single line will be ignored. An example of a single-line
comment is shown near the top of the code listing.
The package Declaration
JavaFX packages are analogous to folders in a file system.
They provide namespaces to logically organize the scripts and classes that comprise
an application. Package names may
consist of more than one node (e.g., com.apress.projavafx). In fact, it is a
common practice for a package name to begin with the domain name of the company
or organization that developed the application (in reverse order, beginning
with the top-level domain name, such as com or org).
The package
declaration is optional, but it is a very good practice to use it in all but
the most trivial programs. If used, the package statement must be at the top of
the source code (excluding whitespace and comments).
import Statements
JavaFX programs typically use libraries that consist of
JavaFX (and optionally Java) code. In this example, each import statement
indicates the location (package) of the JavaFX classes that the code in the
rest of this HelloEarthRiseMain.fx script depends on for rendering the
user interface. An import
statement can end with an asterisk (*), indicating that the program may use any
of the classes in the package. An alternative form is to specifically name each
class being used, as in the example near the top of Listing 1-1:
import javafx.stage.Stage;
All but the most trivial applications should organize their
source code via package
declarations. A source code file uses import statements to indicate its use of
classes contained in source code files that have a different package
statement.
Declarative Code That Defines the User Interface
One of the most exciting features of JavaFX is its ability
to express a graphical user interface (GUI) using a simple, consistent, and
powerful declarative syntax. Declarative code consists of a single expression
(rather than multiple expressions that are executed sequentially).
Note As
you’ll see a little later, JavaFX supports data binding which is characterized
by binding the value of a variable (e.g. the height of a rectangle) to an
expression. Data binding is a major
enabler of using declarative expressions.
In this example, most of the program is declarative, in that
it contains a large expression. This declarative expression begins by defining
a Stage
object followed by an open curly brace, and ends with the matching curly brace
near the end of the program. Nested
within that are instance variables of the Stage object, including the scene variable,
which is assigned a Scene
instance. A Scene has an instance variable named content that
holds the graphical elements that are displayed on the Stage, in this case an ImageView instance
(which displays an image) and a Group instance. Nested within the Group is a content instance variable that holds a Text instance
(which is a graphical element, usually called a graphical node, or
simply node) and so on.
Note This
declarative expression is an example of object literal syntax, in which
instances of these classes are created when the expression is evaluated.
Declarative code automatically creates an instance (also
known as an object) of each JavaFX class in the expression. It also assigns
values to the variables of the new instance. For example, look at the portion
of code that creates an instance of the Rectangle class:
Rectangle {
width: 430
height: 85
}
This code creates an instance of the JavaFX Rectangle class
and assigns the value 430 to the width variable of the new Rectangle instance, and the value 85 to its
height variable.
Notice that the instance variable name is always followed by a colon (:), which in
JavaFX declarative syntax means “assign the value of the expression on the
right to the instance variable on the left.” These same concepts are true for
all of the classes (Stage,
Scene, ImageView, Image, Group, Text, TranslateTransition,
MediaPlayer
and Media)
in this script. Let’s look at each of these classes individually.
Using the Stage Class
A Stage
contains the user interface of a JavaFX app, regardless whether it is deployed
on the desktop, within a browser, or on a mobile device. On the desktop, for example, a Stage has its own
top-level window which typically includes a border and title bar. In the browser the Stage doesn’t have a window, but rather
is rendered as an applet within a rectangular area of the browser.
As with any class, the Stage class has a set of instance
variables. Some of these variables, as shown in the following code snippet from
the listing, are as follows:
* A title that appears in the title bar of
the window (when deployed on the desktop).
* A scene that contains the graphical nodes
in the user interface.
Stage {
title: "Hello
Earthrise"
scene: Scene {
…some code omitted…
}
}
Creating String Literals
One of the basic JavaFX data types is the String, which
consists of zero or more characters strung together. As shown in the following title variable of
the Stage instance,
a String
literal is defined by enclosing a set of characters in double quotes:
title: "Hello Earthrise"
For convenience, as shown in the content variable of the Text instance in
Listing 1-1 above, a String
literal may be defined with multiple sets of quoted characters. Alternatively, String literals may be enclosed in
single quotes. Strings have more capabilities than
described here, but we’ll cover them as they occur in code examples.
Using the Scene Class
As mentioned previously, a Scene holds the graphical elements that
are displayed on the Stage.
Every element in a Scene
is a graphical node, which is any class that extends the javafx.scene.Node class. Take another look at the declarative code,
shown below, that creates the Scene in our example program:
scene: Scene {
content: [
ImageView {
…some code omitted…
},
Group {
…some code omitted…
}
]
}
Notice that the content variable of the Scene is followed
by a left square bracket “[”
which is the literal notation for a sequence. A sequence is a one-dimensional data
structure that is native to JavaFX. Take
a look at the JavaFX API documentation that we showed you how to access in the Accessing
the JavaFX SDK API section above.
You’ll see that the content
variable of the Scene
class is of type Node[],
which means that it can reference a sequence of Node instances.
While you’re looking at the API docs, take a look at the Node class to see
the variables and functions available to any graphical node. Also, take a look at the ImageView class
in the javafx.scene.image
package, and the Group
class in the javafx.scene
package. In both cases, you’ll see that
they inherit from the Node
class.
Tip We
can’t emphasize enough the importance of having the JavaFX API documentation
handy while reading this book. As
classes, variables, and functions are mentioned, it’s often a good idea to look
at the documentation to get more information.
In addition, this habit helps you become more familiar with what is
available to you in the API.
Displaying Images
As shown in the following code, displaying an image entails
using an ImageView
instance in conjunction with an Image instance:
ImageView {
image: Image {
url: "http://projavafx.com/images/earthrise.jpg"
}
},
The Image
instance identifies the image resource and loads it from the URL assigned to
its url
variable. Both of these classes are
located in the javafx.scene.image
package.
Working with Graphical Nodes as a Group
One very powerful graphical feature of JavaFX is the ability
to create scene graphs, which consist of a tree of graphical nodes. You can then assign values to variables of a Group located in
the hierarchy, and the nodes contained in the Group will be affected. In our current example from Listing 1-1,
we’re using a Group
to contain a Text
node and to clip a specific rectangular region within the Group so that the
text doesn’t appear on the moon or the Earth as it animates upward. See the relevant code snippet below:
Group {
layoutX: 50
layoutY: 180
content: [
textRef = Text {
layoutY: 100
textOrigin:
TextOrigin.TOP
textAlignment:
TextAlignment.JUSTIFY
wrappingWidth: 380
content:
"Earthrise at
Christmas: "
...some code
omitted...
fill: Color.#bbc36b
font:
Font.font("SansSerif", FontWeight.BOLD, 24);
}
]
clip:
Rectangle {
width: 430
height: 85
}
}
Notice that the Group is located 50 pixels to the right, and
180 pixels down, from where it would have been located by default. This is due to the values assigned to the layoutX and layoutY variables
of the Group
instance. Because this Group is
contained directly by the Scene,
its upper left corner’s location is 50 pixels to the right and 180 pixels down
from the upper left corner of the Scene.
Take a look at Figure 1-5 below to see this example illustrated as you
read the rest of the explanation.
Figure 1-5. The
Scene, Group, Text, and clip illustrated
Like a Scene,
a Group
instance contains instances of Node subclasses by assigning a sequence of them to the content
variable. In the code snippet above, the
Group
contains a Text
instance which has a value assigned to its layoutY variable. Because this Text is contained by a Group, it assumes
the two-dimensional space (also called the coordinate space) of the Group, with the
origin of the Text node (0,0) being coincident with the top left corner of the
Group. Assigning a value of 100 to the layoutY variable
causes the Text
to be located 100 pixels down from the top of the Group, which is just below the bottom of
the clip region causing it to be out of view until the animation begins. Because a value isn’t assigned to the layoutX variable,
its value is 0 (the default).
The layoutX
and layoutY
variables of the Group
described above is an example of the earlier statement that “you can then
assign values to variables of a Group located in the hierarchy, and the nodes contained
in the Group
will be affected.” Another example of
this is setting the opacity
variable of a Group
instance to 0.5 which causes all of the nodes contained in that Group to become
translucent. If the JavaFX API
documentation is handy, look at the variables available in the javafx.scene.Group
class. Then scroll down to see the
variables inherited from the java.scene.Node class, which is where you’ll find the layoutX, layoutY, and opacity
variables.
Drawing Text
In the snippet above, notice that there are several
variables available in the Text
class. This particular example is a
little more complicated than the normal use of the Text class. Let’s first look at a typical case, shown in
the snippet below, in which you simply want to draw a string of text characters
somewhere in the scene:
Text {
layoutX: 65
layoutY: 12
textOrigin:
TextOrigin.TOP
fill: Color.WHITE
content: "Audio
Configuration"
font:
Font.font("SansSerif", FontWeight.BOLD, 20)
},
This snippet, borrowed from the Audio Configuration example
in Figure 1-16 and Listing 1-3, draws the graphical Text string “Audio Configuration” in a
bold, Sans Serif font. The font size is
20, and the color of the text is white.
Referring again to the JavaFX API documentation, you’ll
notice that the TextOrigin
class (in the javafx.scene.text
package) has three fields that serve as constants: BASELINE, BOTTOM, and TOP.
These control the origin of the text with respect to vertical locations
on the displayed Text:
* The TOP origin, as we’re using it in the
code snippet above, places the top of the text (including ascenders) at the layoutY position,
relative to the coordinate space in which the Text is located.
* The BOTTOM origin would place the bottom of
the text, including descenders (located in a lower case “g” for example) at the
layoutY
position.
* The BASELINE origin would place the baseline
of the text (excluding descenders) at the layoutY position. This is the default value for the textOrigin
variable of a Text
instance.
While you’re looking at the javafx.scene.text package in the API
documentation, take a look at the font function of the Font class, which
is used in the snippet above to define the font family, weight and size of the Text.
Turning back again to the Hello Earthrise example in Listing
1-1, we’re using some additional variables of the Text class that enable it to flow from
one line to the next:
* The wrappingWidth variable enables you to
specify at what number of pixels the text will wrap.
* The textAlignment variable enables you to
control how the text will be justified.
In our example, TextAlignment.JUSTIFY
aligns the text on both the left and right sides, expanding the space between
words to achieve that.
The text that we’re displaying is sufficiently long to wrap
and be drawn on the Earth, so we need to define a rectangular region outside of
which that text can’t be seen:
Clipping Graphical Areas
To define a clipping area, we assign a Node subclass to
the clip
variable that defines the clipping shape, in this case a Rectangle that is
430 pixels wide and 85 pixels high. In
addition to keeping the Text
from covering the moon, when the Text scrolls up as a result of animation, the clipping
area keeps the Text
from covering the earth.
Animating the Text to Make it Scroll Up
When the HelloEarthriseMain.fx script is invoked, the Text begins
scrolling up slowly. To achieve this
animation, we’re using the TranslateTransition
class located in the javafx.animation.transition
package, as shown in the snippet below from Listing 1-1:
// Provides the animated scrolling behavior for the text
var transTransition = TranslateTransition {
duration: 75s
node: bind textRef
toY: -820
interpolator: Interpolator.LINEAR
repeatCount:
Timeline.INDEFINITE
}
...code omitted...
// Start the text animation
transTransition.play();
The javafx.animation.transition
package contains convenience classes for animating nodes. This TranslateTransition instance translates
the Text
node from its original Y position of 100 pixels to a Y position of -820 pixels,
over a duration
of 75 seconds (the duration of the audio clip).
Note that the Text
node is referenced by the textRef
variable as we’ll explain shortly, and that we’re using the bind operator in
the assignment to the node
variable (we’ll cover the bind
operator later in this chapter). The Interpolator.LINEAR
constant is assigned to the interpolator
variable, which causes the animation to proceed in a linear fashion. Looking at the API docs for the Interpolator class
in the javafx.animation
package will reveal that there are other forms of interpolation available, one
of which is EASEOUT,
which slows down the animation toward the end of the specified duration.
Note Interpolation in
this context is the process of calculating the value at any point in time,
given a beginning value, an ending value, and a duration.
The last line in the snippet above begins executing the play function of
the TranslateTransition
instance created earlier in the script, which makes the Text begin scrolling upward. Because of the value assigned to the repeatCount
variable, this transition will repeat indefinitely.
Take a look again at the entire HelloEarthRiseMain.fx script
in Listing 1-1 to get an overall picture of how it is processed when invoked:
* The first statement declares a variable of
type Text,
named textRef,
which is used to hold a reference to the Text instance that will have a
transition applied to it (as we just finished discussing).
* The second statement declares a variable
named transTransition
and creates an instance of the TranslateTransition class, assigning its reference to the
transTransition
variable.
* Next, the declarative expression that
comprises most of the rest of the script
is evaluated, which creates an instance of the Stage class and the other classes in the
UI scene graph.
* The statement at the end of the script is
executed, which plays the transition.
Finally, let’s discuss how the audio media is played as the
text is scrolling.
Playing Media
The code snippet below from Listing 1-1 demonstrates how to
load and play audio media:
MediaPlayer {
autoPlay: true
repeatCount: MediaPlayer.REPEAT_FOREVER
media: Media {
source:
"http://projavafx.com/audio/zarathustra.mid"
}
}
As shown above, the audio clip automatically begins playing,
and repeats indefinitely (until the program exits). We’ll have a lot more to say about playing
audio and video media in Chapter 6, so stay tuned!
Now that you’ve compiled and run this example using the
command-line tools, and we’ve walked through the code together, it is time to
begin using the NetBeans IDE with the JavaFX plug-in to make the development
and deployment process faster and easier.
Building and Running the Program with NetBeans
Assuming that you’ve downloaded and extracted the source
code for this book into a directory, follow the directions in this exercise to
build and run the Hello Earthrise program in NetBeans with the JavaFX
Plug-in. If you haven’t yet downloaded
the version of the JavaFX SDK that contains NetBeans, please do so from the
JavaFX downloads page listed in the Resources section at the end of this chapter.
Building and Running Hello Earthrise with NetBeans
To build and run the Hello Earthrise program, perform the
following steps:
1. Start up the version of NetBeans that is
available for download with the JavaFX SDK. .
2. Choose File ~TRA New Project
from the menu bar. The following dialog
in Figure 1-6 should appear:
Figure 1-6. The
NetBeans New Project Wizard
3. Choose JavaFX in the Categories
pane, JavaFX Script Application in the Projects
pane, and select the Next button. The next page in the New Project Wizard
should appear, as shown in Figure 1-7 below:
Figure 1-7. New
JavaFX Script Application Dialog
4. In the dialog in Figure 1-7, type the Project
Name (we used HelloEarthrise) click the Browse button.
5. In the Select Project Location
dialog box (not shown), navigate to the directory in which you’d like to create
this project (we used C:\MyJavaFX), and select the Open
button.
6. Select the From Sources
radio button and click the Add Folder button.
7. In the Browse Source Packages
Editor dialog (not shown), navigate to the
Chapter01/HelloEarthrise/src directory subordinate to where you expanded this
book’s code download bundle, and select the Open button.
Note: These instructions are for creating a new
NetBeans project from JavaFX source code.
Step 7 above won’t be successful if NetBeans finds a directory named nbproject
and/or a build.xml file. This
is because Netbeans assumes that the src directory
is part of another project and refuses to add them to the new project. The build/run instructions in the Audio
Configuration example later in this chapter demonstrate how to work with a
NetBeans JavaFX project that already exists.
8. Select the Finish
button. The HelloEarthrise project
should now be created, and you’re ready to run it.
9. Right-click on the HelloEarthrise project in
the Projects pane and select Run Project
from the context menu.
10. The Run Project dialog, shown in Figure 1-8
below, will appear the first time you run the project, asking for the main
class. Choose the projavafx.helloearthrise.ui.HelloEarthRiseMain class
(the only class in the list for this simple example), and select the OK
button.
Figure 1-8.
Choosing the main class for a project
The HelloEarthRise
program should begin executing, with an appearance similar to the screenshot in
Figure 1-4, shown earlier in the chapter.
At this point, you’ve built and run the “Hello Earthrise”
program application – both from the command line, and using NetBeans. Now we’re going to show you how to deploy
this program using Java Web Start, and then as an applet in a browser.
Deploying JavaFX Applications
As mentioned earlier in this chapter, the JavaFX and
Java SE 6 update 10 technologies are working together to restore rich client
Java. In this section you’ll configure
and deploy the Hello Earthrise application with Java Web Start, and as a JavaFX
applet. The underlying features of Java
SE 6 Update 10 will be leveraged in this process.
Deploying JavaFX Applications with Java Web Start
To build the “Hello Earthrise” program with NetBeans to
deploy as a Java Web Start application, you need to create a configuration for
that purpose. The following exercise
walks you through this process.
Deploying the Hello Earthrise program with Java Web Start
To create a configuration for deploying the Hello
Earthrise program with Java Web Start, perform the following steps:
1. Right-click on the HelloEarthrise project in
the Projects pane and select Set Configuration ~TRA
Customize from the context menu, as shown in Figure 1-9 below:
Figure 1-9.
Customizing a configuration from the context menu
2. The Project Properties dialog, shown in
Figure 1-10 below, will appear. Choose Run
from the Categories pane, and the Web Start Execution
radio button from the Application Execution Model options.
Figure 1-10.
Selecting Web Start Execution
3. While still in the Project Properties dialog,
choose Application from Categories
pane as shown in Figure 1-11 below.
Select the Self Signed Jar and Pack200
Compression check boxes.
The Applet specific properties are not applicable for Web
Start execution, so you can safely ignore these for now. Select the OK button to
close this dialog.
Figure 1-11.
Choosing Web Start specific properties
Caution Selecting
Self Signed Jar signs the Web-started application, but it doesn’t use an
authoritative certificate. A security
dialog will appear, but it will indicate that the signature can’t be
verified. For this reason, posting (or
executing) JavaFX applications with self-signed JARs on the Internet is not a
good practice. If the application
doesn’t require access outside the sandbox (e.g. to the Nasa website as this
one does), then there is no need to sign the code anyway. If the application needs access outside the
sandbox and you want to put your application on the Internet, then we suggest
that you get a code-signing certificate (e.g. from VeriSign or Thawte).
Because of the configuration that you just performed,
when you run the project as described previously, it should be invoked via Java
Web Start, with its appearance as shown in Figure 1-4 above.
Now that you’ve configured and run Hello Earthrise as a Java
Web Start application, let’s deploy it as an applet inside your Web browser.
Deploying JavaFX Applets
To deploy the “Hello Earthrise” program as a Java Web Start
application, you need to create a configuration in the NetBeans project for
that purpose. The following exercise
walks you through this process:
Deploying the Hello Earthrise program as an Applet in a
Web Browser
To create a configuration for deploying the Hello
Earthrise program as an applet in a browser, perform the following steps:
1. Right-click on the HelloEarthrise project in
the Projects pane and select Set Configuration ~TRA
Customize from the context menu, as shown in Figure 1-9
previously.
2. The Project Properties dialog, shown in
Figure 1-12 below, will appear. Choose Run
from the Categories pane, and the Run in Browser
radio button from the Application Execution Model options.
Figure 1-12.
Choosing to Run in Browser
3. While still in the Project Properties dialog,
choose Application from Categories
pane as shown in Figure 1-13 below.
Select the Self Signed Jar and Pack
200 Compression check boxes.
In Applet specific properties, enter 516 in the Width
field, 387 in the Height field, and select the Draggable
Applet check box. The
reason for these particular width and height numbers is that these are the
dimensions of the image which fills the scene.
Choosing to make the applet draggable, when run in a browser on which
Java SE 6 Update 10 is installed, allows that applet to be dragged from the browser
onto the desktop. Select the OK
button to close this dialog.
Figure 1-13.
Choosing Applet specific properties
Because of the configuration that you just performed,
when you run the project as described previously, it should be invoked in your
default browser. If you have Java SE 6
update 10 installed, then hold the ALT key down while clicking and dragging the
applet onto the desktop as shown in Figure 1-14 below.
Figure 1-14.
Dragging the Hello Earthrise applet onto the desktop
Before leaving this example, we’d like to show you another
way to clip the scrolling Text node.
There is a class in the javafx.scene.layout package named ClipView whose purpose is to provide a
clipped view of a node that is typically larger than the view. In addition, the user can drag the node being
viewed within the clipped area. Figure
1-15 below is a screenshot of the Hello Earthrise program after being modified
to use the ClipView layout container.
Figure 1-15.
Using the ClipView layout container to clip the Text node
Notice that the move cursor is visible, signifying
that the user can drag the node around the clipped area. Note: The screenshot in Figure 1-15 is of the
program running on Windows, and the move cursor has a different
appearance on other platforms. Listing
1-2 below contains the code for this example, named HelloClipViewExample:
Listing 1-2. The HelloClipViewExample.fx Program
package projavafx.helloclipview.ui;
import javafx.animation.transition.TranslateTransition;
import javafx.animation.*;
import javafx.stage.Stage;
import javafx.scene.*;
import javafx.scene.image.*;
import javafx.scene.layout.ClipView;
import javafx.scene.media.*;
import javafx.scene.paint.Color;
import javafx.scene.text.*;
var textRef:Text;
// Provides the animated scrolling behavior for the text
var transTransition = TranslateTransition {
duration: 75s
node: bind textRef
toY: -820
interpolator: Interpolator.LINEAR
repeatCount: Timeline.INDEFINITE
}
Stage {
title: "Hello ClipView"
scene: Scene {
height: 387
width: 516
content: [
ImageView {
image: Image {
url: "http://projavafx.com/images/earthrise.jpg"
}
},
ClipView {
layoutX: 50
layoutY: 180
width: 430
height: 85
node:
textRef = Text {
layoutY: 100
textOrigin: TextOrigin.TOP
textAlignment: TextAlignment.JUSTIFY
wrappingWidth: 380
// Note that this syntax creates one long string of text
content: "Earthrise at Christmas: "
"[Forty] years ago this Christmas, a turbulent world "
"looked to the heavens for a unique view of our home "
"planet. This photo of Earthrise over the lunar horizon "
"was taken by the Apollo 8 crew in December 1968, showing "
"Earth for the first time as it appears from deep space. "
"Astronauts Frank Borman, Jim Lovell and William Anders "
"had become the first humans to leave Earth orbit, "
"entering lunar orbit on Christmas Eve. In a historic live "
"broadcast that night, the crew took turns reading from "
"the Book of Genesis, closing with a holiday wish from "
"Commander Borman: \"We close with good night, good luck, "
"a Merry Christmas, and God bless all of you -- all of "
"you on the good Earth.\""
// The approximate color used in the scrolling Star Wars intro
fill: Color.web("0xBBC36B")
font: Font.font("SansSerif", FontWeight.BOLD, 24);
}
}
]
}
}
// Start playing an audio clip
MediaPlayer {
autoPlay: true
repeatCount: MediaPlayer.REPEAT_FOREVER
media: Media {
source: "http://projavafx.com/audio/zarathustra.mid"
}
}
// Start the text animation
transTransition.play();
Later chapters will cover other layout containers in the javafx.scene.layout
package, such as HBox, VBox, Flow, Tile, and Stack. For now, let’s examine another JavaFX example
application to help you learn more JavaFX Script concepts and constructs.
Developing Your Second JavaFX Program: "More Cowbell!"
If you're familiar with the Saturday Night Live television
show, you may have seen the More Cowbell sketch, in which Christopher Walken's
character keeps asking for "more cowbell" during a Blue Oyster Cult
recording session. The following JavaFX
example program covers some of the simple but powerful concepts of JavaFX in
the context of an imaginary application that lets you select a music genre and
control the volume. Of course, "Cowbell
Metal", shortened to "Cowbell", is one of the available genres. Figure 1-16 shows a screenshot of this
application, which has a sort of iPhone application look.
Figure 1-16. The
Audio Configuration “More Cowbell” Program
Building and Running the Audio Configuration Program
Earlier in the chapter, we showed you how to create a new
JavaFX project in NetBeans, and how to add a folder that contains source code
files to the project.
For this example (and the rest of the examples in the book),
we’ll take advantage of the fact that the code download bundle for the book
contains both NetBeans and Eclipse project files for each example. Follow the instructions in the exercise below
to build and run the Audio Configuration application.
Building and Running the Audio Configuration Program
using NetBeans
To build and execute this program using NetBeans, perform
the following steps:
1. From the File menu,
select the Open Project menu item.
In the Open Project dialog, navigate to the
Chapter01 directory subordinate to where you extracted the book’s code download
bundle, as shown in figure 1-17 below:
Figure 1-17.
Opening an existing project in NetBeans
2. Select the AudioConfig project in the pane on
the left, and select the Open Project button.
3. Run the project as discussed previously.
The application should appear as shown in Figure 1-16
above.
The Behavior of the Audio Configuration Program
When you run the application, notice that adjusting the
volume slider changes the associated decibel (dB) level displayed. Also, selecting the Muting
checkbox disables the slider, and selecting various genres changes the volume
slider. This behavior is enabled by
concepts that you'll see in the code below, such as binding to a class that
contains a model, on replace triggers, and sequences.
Understanding the Audio Configuration Program
The Audio Configuration program contains two source code
files, shown below in Listing 1-3 and Listing 1-4:
* The AudioConfigMain.fx file in Listing 1-3
contains the main script, and expresses the UI in a manner that you are
familiar with from the Hello Earthrise example in Listing 1-1.
* The AudioConfigModel.fx file in Listing 1-4
contains a model for this program, which holds the state of the application, to
which the UI is bound.
Take a look at the AudioConfigMain.fx source code in Listing
1-3, and then we’ll examine it together, focusing on concepts not covered in
the previous example:
Listing 1-3. The AudioConfigMain.fx Program
package projavafx.audioconfig.ui;
import javafx.ext.swing.SwingComboBox;
import javafx.ext.swing.SwingComboBoxItem;
import javafx.scene.*;
import javafx.scene.control.Slider;
import javafx.scene.control.CheckBox;
import javafx.scene.paint.*;
import javafx.scene.shape.*;
import javafx.scene.text.*;
import javafx.stage.Stage;
import projavafx.audioconfig.model.AudioConfigModel;
Stage {
def acModel = AudioConfigModel {
selectedDecibels: 35
}
title: "Audio Configuration"
scene: Scene {
content: [
Rectangle {
// No need to assign 0 to x and y, because 0 is the default
width: 320
height: 45
fill: LinearGradient {
// No need to assign 0 to startX and startY, because 0 is default
endX: 0.0
endY: 1.0
stops: [
Stop {
color: Color.web("0xAEBBCC")
offset: 0.0
},
Stop {
color: Color.web("0x6D84A3")
offset: 1.0
}
]
}
},
Text {
layoutX: 65
layoutY: 12
textOrigin: TextOrigin.TOP
fill: Color.WHITE
content: "Audio Configuration"
font: Font.font("SansSerif", FontWeight.BOLD, 20)
},
Rectangle {
x: 0 // 0 is default, so assigning here just for clarity
y: 43
width: 320
height: 300
fill: Color.web("0xC7CED5")
},
Rectangle {
x: 9
y: 54
width: 300
height: 130
arcWidth: 20
arcHeight: 20
fill: Color.WHITE
stroke: Color.color(0.66, 0.67, 0.69)
},
Text {
layoutX: 18
layoutY: 69
textOrigin: TextOrigin.TOP
fill: Color.web("0x131021")
content: bind "{%1.0f acModel.selectedDecibels} dB"
font: Font.font("SansSerif", FontWeight.BOLD, 18)
},
Slider {
layoutX: 135
layoutY: 69
width: 162
disable: bind acModel.muting
min: bind acModel.minDecibels
max: bind acModel.maxDecibels
value: bind acModel.selectedDecibels with inverse
},
Line {
startX: 9
startY: 97
endX: 309
endY: 97
stroke: Color.color(0.66, 0.67, 0.69)
},
Text {
layoutX: 18
layoutY: 113
textOrigin: TextOrigin.TOP
fill: Color.web("0x131021")
content: "Muting"
font: Font.font("SansSerif", FontWeight.BOLD, 18)
},
CheckBox {
layoutX: 280
layoutY: 113
selected: bind acModel.muting with inverse
},
Line {
startX: 9
startY: 141
endX: 309
endY: 141
stroke: Color.web("0xA8AAAF")
},
Text {
layoutX: 18
layoutY: 157
textOrigin: TextOrigin.TOP
fill: Color.web("0x131021")
content: "Genre"
font: Font.font("SansSerif", FontWeight.BOLD, 18)
},
SwingComboBox {
layoutX: 204
layoutY: 148
width: 93
items: bind for (genre in acModel.genres) {
SwingComboBoxItem {
text: genre
}
}
selectedIndex: bind acModel.selectedGenreIndex with inverse
}
]
}
}
Now that you’ve had a look at
the main script in this application, let’s walk through the new concepts.
Creating an Instance of the Model, and the
Magic of Binding
One of the very powerful aspects of JavaFX is binding, which
enables the application’s UI to easily stay in sync with the state, or model,
of the application. The model for a
JavaFX application is typically held in one or more classes, in this case the AudioConfigModel
class. To make an instance of this class
we use the same object literal syntax that we’ve used to create instances of UI
classes. Look at the snippet below,
taken from Listing 1-3:
def acModel =
AudioConfigModel {
selectedDecibels: 35
}
This instantiates the AudioConfigModel class, initializing its
selectedDecibels
instance variable to a value of 35. In
addition, a reference to that instance is held by a variable named acModel.
Note: The def keyword in the snippet above is
similar to var,
except that its value must be assigned and initialized when it is
declared. Also, it cannot subsequently
be assigned a value. The def keyword
enables the compiler to perform related optimizations.
There are several graphical node instances in the scene of
this UI (recall that a scene consists of a sequence of nodes). Skipping past several of them, we come to the
graphical nodes shown in the snippet below that have an instance variable bound
to the selectedDecibels
variable in the model:
Text {
layoutX: 18
layoutY: 69
textOrigin:
TextOrigin.TOP
fill:
Color.web("0x131021")
content: bind
"{%1.0f acModel.selectedDecibels} dB"
font:
Font.font("SansSerif", FontWeight.BOLD, 18)
},
Slider {
layoutX: 135
layoutY: 69
width: 162
disable: bind
acModel.muting
min: bind acModel.minDecibels
max: bind
acModel.maxDecibels
value: bind
acModel.selectedDecibels with inverse
},
As shown in the snippet above, the content variable of the Text object is
bound to a String. The curly braces in
this String contain an expression (that includes the selectedDecibels variable) which is
evaluated and becomes part of the String.
Look at Figure 1-16 (or check the running application) to see the content value of
the Text
node displayed to the left of the slider.
Note: The %1.0f string
formatting specification causes the decimal portion of selectedDecibels
to be suppressed. See Chapter 4 for
details on string formatting and its use for internationalization.
Notice also in the snippet above that value instance
variable of the Slider
node is bound to the selectedDecibels
variable in the model as well, but that it has the keywords with inverse at
the end of the expression. This causes
the bind to be bi-directional, so in this case when the slider is moved, the selectedDecibels value
in the model changes. Conversely, when
the selectedDecibels
value changes (as a result of changing the genre), the slider moves.
Go ahead and move the slider to demonstrate the effects of
the bind expressions in the snippet above.
The number of decibels displayed at the left of the slider should change
as the slider is adjusted.
There are other bound instance variables in Listing 1-3 that
we’ll point out when we walk through the model class. Before leaving the UI, we’d like to point out
some color-related concepts in this example.
Colors and Gradients
The snippet below from Listing 1-3 contains an example of
defining a color gradient pattern, as well as defining colors:
Rectangle {
// No need to
assign 0 to x and y, because 0 is the default
width: 320
height: 45
fill:
LinearGradient {
// No need to
assign 0 to startX and startY, because 0 is default
endX: 0.0
endY: 1.0
stops: [
Stop {
color: Color.web("0xAEBBCC")
offset: 0.0
},
Stop {
color:
Color.web("0x6D84A3")
offset:
1.0
}
]
}
},
If the JavaFX API docs are handy, first take a look at the javafx.scene.shape.Rectangle
class and notice that it inherits an instance variable named fill that is of
type javafx.scene.paint.Paint. Looking at the JavaFX API docs for the Paint class,
you’ll see that the Color,
LinearGradient,
and RadialGradient
classes are subclasses of Paint. This means that the fill of any shape can be
assigned a color or a gradient.
To create a LinearGradient, as shown in the snippet, you need to
define at least two stops, which define the location and color at that location. In this example the offset value of the first stop is 0.0,
and the offset
value of the second stop is 1.0. These
are the values at both extremes of the unit square, the result being
that the gradient will span the entire node (in this case a Rectangle). The direction of the LinearGradient is controlled by its startX, startY, endX and endY values. In this case, the direction is only vertical
because the startY
value is 0.0 and the endY
value is 1.0, while the startX
and endX
values are both 0.0.
Note that in the Hello Earthrise example in Listing 1-1, the
constant named Color.WHITE
was used to represent the color white.
In the snippet above, the web function of the Color class is used to define a color
from a hexadecimal value.
The Model Class for the Audio Configuration Example
Take a look at the source code for the AudioConfigModel class
in Listing 1-4 below:
Listing 1-4. The Source Code for AudioConfigModel.fx
package projavafx.audioconfig.model;
/**
* The model class that the AudioConfigMain.fx script uses
*/
public class AudioConfigModel {
/**
* The minimum audio volume in decibels
*/
public def minDecibels:Number = 0;
/**
* The maximum audio volume in decibels
*/
public def maxDecibels:Number = 160;
/**
* The selected audio volume in decibels
*/
public var selectedDecibels:Number;
/**
* Indicates whether audio is muted
*/
public var muting:Boolean; // false is default for Boolean
/**
* List of some musical genres
*/
public def genres = [
"Chamber",
"Country",
"Cowbell",
"Metal",
"Polka",
"Rock"
];
/**
* Index of the selected genre
*/
public var selectedGenreIndex:Integer on replace {
if (genres[selectedGenreIndex] == "Chamber") {
selectedDecibels = 80;
}
else if (genres[selectedGenreIndex] == "Country") {
selectedDecibels = 100;
}
else if (genres[selectedGenreIndex] == "Cowbell") {
selectedDecibels = 150;
}
else if (genres[selectedGenreIndex] == "Metal") {
selectedDecibels = 140;
}
else if (genres[selectedGenreIndex] == "Polka") {
selectedDecibels = 120;
}
else if (genres[selectedGenreIndex] == "Rock") {
selectedDecibels = 130;
}
};
}
Structure of a Minimal JavaFX class
JavaFX is fully object-oriented, and even supports mixin
inheritance and closures (both of which will be discussed in
Chapter 4). The minimum code that you
need to create a class in JavaFX is the class declaration. The declaration of a class always includes
the class
keyword and, as shown in the preceding code snippet, has opening and closing
curly braces. There are other JavaFX keywords, such as public and extends, that can modify the class keyword.
We’ll discuss these in detail in Chapter 4.
Another basic construct of a class declaration is the
instance variable. The class in Listing
1-4 defines several instance variables.
When an instance of this class is created, space is carved out in memory
for each instance variable.
Defining Triggers in the Model Class
Triggers are a construct in JavaFX that helps enable
declarative programming. For example,
the on replace
trigger shown in the snippet below executes whenever the value of the selectedGenreIndex
variable changes:
public var
selectedGenreIndex:Integer on replace {
if
(genres[selectedGenreIndex] == "Chamber") {
selectedDecibels =
80;
}
else if
(genres[selectedGenreIndex] == "Country") {
selectedDecibels =
100;
}
else if
(genres[selectedGenreIndex] == "Cowbell") {
selectedDecibels =
150;
}
else if
(genres[selectedGenreIndex] == "Metal") {
selectedDecibels =
140;
}
else if
(genres[selectedGenreIndex] == "Polka") {
selectedDecibels =
120;
}
else if
(genres[selectedGenreIndex] == "Rock") {
selectedDecibels =
130;
}
};
What causes selectedGenreIndex to change though? To see the answer to this, we have to revisit
the declarative UI script in Listing 1-3.
Here’s the relevant snippet:
SwingComboBox {
layoutX: 204
layoutY: 148
width: 93
items: bind for
(genre in acModel.genres) {
SwingComboBoxItem
{
text: genre
}
}
selectedIndex: bind
acModel.selectedGenreIndex with inverse
}
Notice that the selectedIndex variable of the SwingComboBox is
bi-directionally bound (recall our with inverse discussion) to the selectedGenreIndex
variable in the model. Because of this,
whenever the user selects a different genre in the combo box, the on replace trigger
is executed. Looking again at the code
in the on replace trigger, you’ll see that the value of the selectedDecibels
variable changes, which as you may recall, is bi-directionally bound to the
slider. This is why the slider moves
when you select a genre in the combo box.
Go ahead and test this out by running the Audio Config program.
We’ll point out one more example of binding in this program,
and then we’ll let you study the code independently to find the rest. In the most recent code snippet, binding is
used to populate the SwingComboBox
with SwingComboBoxItem
instances that each contains a genre.
The snippet below from the model code in Listing 1-4 contains the
sequence to which the SwingComboBoxItem
instances are bound.
/**
* List of some musical
genres
*/
public var genres = [
"Chamber",
"Country",
"Cowbell",
"Metal",
"Polka",
"Rock"
];
The means by which the genres sequence is bound to the SwingComboBox items variable
(using the for
keyword in the explicit sequence expression) will be covered in
Chapter 2.
Surveying JavaFX Features
We’d like to close this chapter by surveying many of the
features of JavaFX, some of which will be a review for you. We’ll do this by describing several of the
more commonly used packages and classes in the JavaFX SDK API:
The javafx.stage
package contains:
* The
Stage
class, which is the top level of the UI containment hierarchy for any JavaFX
application, regardless of where it is deployed (e.g. desktop, browser, or
mobile phone).
* The Alert class, which has functions that
may be called to make alert dialogs (such as a confirmation dialog) appear.
* The Screen class represents the displays on the machine in
which a JavaFX program is running. This
enables you to get information about the screens, such as size and resolution.
The javafx.scene
package contains some classes that you'll use often:
* The Scene class is the second level of the
UI containment hierarchy for JavaFX applications. It contains all of the UI elements contained
in the application. These elements are
called graphical nodes, or simply nodes.
* The Node class is the base class of all of
the graphical nodes in JavaFX. As you'll
see, UI elements such as text, images, media, shapes, and controls (such as text
boxes and buttons) are all subclasses of Node.
Take a moment to look at the variables and functions in the Node class to
appreciate the capabilities provided to all of its subclasses, including bounds
calculation and mouse and keyboard event handling.
* The CustomNode class enables you to create
your own UI elements.
* The Group class is a subclass of the Node class whose
purpose includes grouping nodes together into a single coordinate space, and
allowing transforms (such as rotate) to be applied to the whole group. Also, attributes of the group that are
changed (such as opacity) apply to all of the nodes contained within the group.
There are several packages that begin with javafx.scene that
contain subclasses of Node
of various types. For example:
* The javafx.scene.image package contains the Image and ImageView
classes, which enables images to be displayed in the Scene.
The ImageView
class is a subclass of Node.
* The javafx.scene.shape package contains
several classes for drawing shapes such as Circle, Rectangle, Line, Polygon and Arc.
The base class of the shapes, named Shape, contains an attribute named fill that enables
you to specify a color or gradient with which to fill the shape.
* The javafx.scene.text package contains the Text class for
drawing text in the scene. The Font class
enables you to specify the font name and size of the text.
* The javafx.scene.media package has classes
that enable you to play media. The MediaView class
is a subclass of Node
that displays the media.
* The javafx.scene.chart package has classes
that help you easily create area, bar, bubble, line,
pie, and scatter charts.
The corresponding UI classes in this package are AreaChart, etc.
Although it doesn't begin with javafx.scene, the javafx.ext.swing
package contains UI controls (such as SwingCombobox) that subclass Node. Many of the Swing "components" are
available in this package, and because these components are actually nodes, any
of the transforms and other capabilities of Node are available with these
classes. Also, any Swing components that
aren't in this package may be used in JavaFX by wrapping them with the wrap function of
the SwingComponent
class.
The javafx.scene.control
package contains several UI controls,
each one having the ability to be skinned, and styled via CSS. The Control, Skin, and Behavior classes in this package give
you the ability to create your own skinned and CSS-styled controls.
The javafx.scene.transform
package enables you to transform nodes (scale, rotate, translate, shear, and
affine).
The javafx.scene.input
package contains classes such as MouseEvent and KeyEvent that provide information about
these events from within an event handler function such as the Node class' onMouseClicked
event.
The javafx.scene.layout
package contains several layout containers, including HBox, VBox, ClipView, Flow, Stack, and Tile .
The Container
class in that package enables writing custom layout classes.
The javafx.scene.effect
and javafx.scene.effect.light
packages contain easy to use effects such as Reflection, Glow, Shadow, BoxBlur and Lighting.
The javafx.animation
and javafx.animation.transition
packages contain time-based interpolations typically used for animation, and
convenience classes for common transitions, respectively.
The javafx.io.http,
javafx.data.pull,
and javafx.data.xml
packages provide a way to communicate with servers via HTTP, JSON, and XML from
any JavaFX enabled device (e.g. desktop and mobile phones).
The javafx.data.feed.atom,
and javafx.data.feed.rss
packages contain classes that facilitate reading Atom and RSS feeds,
respectively.
The javafx.async
package provide support for asynchronous processing. Classes in this package are extended by
classes such as HttpRequest
to provide asynchronous capability.
The javafx.io
package has classes that utilize local storage facilities of a machine without
requiring that a JavaFX program be signed.
The javafx.util
package contains several utility classes such as Math, Sequences, and Properties.
Take a look at the JavaFX API docs again in the light of the
information above to get a deeper sense for how you can use its capabilities.
Recent Comments