javafx
Pet Catalog Photos : JavaFX Example Application
Pet Catalog Photos is a simple JavaFX application that displays pet
photos retrieved from a RESTful Pet Catalog app (implemented using
JAX-RS) described in an earlier blog
entry and in this screencast.
This JavaFX example is a modification of the Interesting
Photos : JavaFX Example Application.
Understanding the Code
Code Sample from: main.fx |
//
Application User Interface
def stage = Stage {
title: "Pet Catalog"
resizable: false
x: bind stageX with inverse
y: bind stageY with inverse
width: stageWidth
height: stageHeight
visible: true
style: StageStyle.TRANSPARENT
scene: Scene {
content: Group {
content: bind stageContent
clip: Rectangle {
width: stageWidth
height: stageHeight
arcWidth: 20
arcHeight: 20
}
}
fill: Color.TRANSPARENT
}
}
|
Stage is the top level container window required to
display any visible JavaFX objects. The variables
title,
width
and
height
define the the text that appears on the window's top border and its
height and width.
The
scene variable defines an instance of the Scene
object literal, which sets the area in which you can place the JavaFX
objects.
Scene is a drawing surface for graphical content and a
container that holds the scene graph nodes. It can be added to
Stage,
and JavaFX renders everything on a scene.
The
scene instance
variable has a
content
variable that is used to hold JavaFX graphical elements and defines the
graphical content of the application. Here the content consists of a
Group
Node . The
Group Node
has
a
content
variable which is a sequence of child Nodes that will be rendered in
order whenever this Group is rendered. The
Group Node has
a
clip
variable which specifies a Rectangle which defines the the clipping
shape for this
Group Node.
The
Group Node
content
variable has a data binding with
stageContent.
Data binding
allows creating a direct and immediate relationship
between two variables or between a variable and the outcome of a
function or an expression.
stageContent is a sequence of Nodes as shown in the code
below.
Node - is an element in a scene graph. The
following visual objects are examples of
javafx.scene.Node
implementations:
javafx.scene.image.ImageView,
javafx.scene.media.Mediaview,
javafx.ext.swing.*,
javafx.scene.shape.*,
and
javafx.scene.text.Text. These are leaf nodes, which
cannot have a child element.
Code Sample from: main.fx |
//
Application User Interface
var stageContent: Node[];
stageContent = [
bgImage, titleBar,
nextButton, backButton, closeButton,
titleText, statusText,
thumbImageViewGroup, fullImageView
];
|

A grid of
ImageView is added to
Scene of
Stage.
The
thumbImageViewGroup variable has a data
binding with
thumbImageViews which is a sequence of
ThumbImageView
which extends
ImageView.
ImageView is a
Node used for painting
images loaded with
Image
class. The code below shows how the
thumbImageViews sequence
is initialized to a grid of 3 columns and 3 rows of
ThumbImageView
.
Code Sample from: main.fx |
var thumbImageViews: ThumbImageView[]; // Thumbnail
images
for(col in [0..2]) {
for(row in [0..2]) {
def thumbImageView =
ThumbImageView {
x:
thumbBaseX + (col * (thumbSize + thumbSpace))
y:
thumbBaseY + (row * (thumbSize + thumbSpace))
fitWidth: thumbSize
fitHeight: thumbSize
}
insert thumbImageView into
thumbImageViews;
}
}
var thumbImageViewGroup = Group {
content: bind thumbImageViews
}
|
calling the RESTful Pet Catalog Web Service
Information and URLs for pet photos is obtained by performing an HTTP
GET request on a RESTful Catalog service by using the JavaFX
asynchronous HTTP API
(
javafx.io.http.HttpRequest).
HttpRequest
allows one to specify a
location and
method
and start a HTTP operation with the function
enqueue().
The content of the HTTP response can be accessed in the
onInput
callback function.
onInput calls the
PhotoPullParser.parse
function to parse the XML.The
onDone Callback is invoked
when the request has finished execution,
onDone calls
the
updateImages() function, explained later, which
updates the images displayed in the Photo-Grid.
Code Sample from: main.fx |
function loadImageMetadata() {
var start=page * 9;
var request: HttpRequest = HttpRequest {
location:
"http://localhost:8080/catalog/resources/items/?start={start}&max=9"
method: HttpRequest.GET
onInput: function(input:
java.io.InputStream)
{
var parser = PhotoPullParser{};
photos = parser.parse(input);
}
onDone: function() {
updateImages();
}
}
request.enqueue();
}
|
The response XML document contains a list of
information about available photos. The document
contains the following information about each photo:
- id
imagethumburl
imageurl
- server
name
price
productid
description
Here is example XML returned from the RESTFul Catalog Service:
<items
uri="http://localhost:8080/catalog/resources/items/">
<item
uri="http://localhost:8080/catalog/resources/items/1/">
<description>This black and white colored cat is super
friendly./description>
<id>1</id>
<imagethumburl>http://localhost:8080/catalog/images/anthony-s.jpg</imagethumburl>
<imageurl>http://localhost:8080/catalog/images/anthony.jpg</imageurl>
<name>Friendly Cat</name>
<price>307.10</price>
<productid>feline01</productid>
</item>
...
</items>
The
response document is
parsed by using the JavaFX XML pull parser
(
javafx.data.pull.PullParser) to
extract information about
the photos.
The parser supplies a sequence of Events as it process the document
under application control. XML and JSON are the two data formats
currently supported. The PullParser can be used with the
onEvent
callback, which reports the current parse event. The
onEvent
callback shown below parses the xml
item elements
into an instance of the
Photo class. When the item
END_ELEMENT
, </item>, is reached, the
photo
variable is inserted into the
photos sequence.
Code Sample from: PhotoPullParser.fx
|
import javafx.data.pull.PullParser;
public class PhotoPullParser {
public function parse(input: InputStream): Photo[] {
// Information about all catalog photos
var photos: Photo[];
var photo: Photo;
// Parse the input data (Photo Metadata) and
construct Photo instance
def parser = PullParser {
input: input
onEvent: function(event:
Event) {
if
(event.type == PullParser.START_ELEMENT) {
if(event.qname.name == "item" and event.level == 1) {
photo = Photo { };
}
}
else
if
(event.type == PullParser.END_ELEMENT) {
if(event.qname.name == "item" and event.level == 1) {
insert photo into photos;
} else
if(event.qname.name == "id" and event.level == 2) {
photo.id = event.text;
} else
if(event.qname.name == "name" and event.level == 2) {
photo.name = event.text;
} else
if(event.qname.name == "imagethumburl" and event.level == 2) {
photo.imagethumburl = event.text;
} else
if(event.qname.name == "imageurl" and event.level == 2) {
photo.imageurl = event.text;
} else
if(event.qname.name == "description" and event.level == 2) {
photo.description = event.text;
} else
if(event.qname.name == "productid" and event.level == 2) {
photo.productid = event.text;
}
}
}
}
parser.parse();
return photos;
}
}
|
A sequence of photo objects is constructed by parsing
the response
XML.
Code Sample from: Photo.fx |
public class Photo {
public var id: String;
public var imagethumburl: String;
public var imageurl: String;
public var name: String;
public var price: String;
public var description: String;
public var productid: String;
}
|
The
onDone Callback is invoked when the
HttpRequest
has finished execution,
onDone calls the
updateImages()
function, shown below.
Code Sample from: main.fx |
// Load image and data specified in given Photo object
function loadImage(photo: Photo, thumbImageView: ThumbImageView): Void
{
thumbImageView.image = Image {
url: "{photo.imagethumburl}";
width: thumbSize
height: thumbSize
backgroundLoading: true
placeholder:
thumbImageView.image
};
thumbImageView.photo = photo;
// Update images displayed in Photo-Grid
function updateImages() {
for(i in [0..8]) {
var photoIndex = i;
loadImage(photos[photoIndex], thumbImageViews[i]);
}
}
|
updateImages updates
the images displayed in the
thumbImageViewGroup by
updating the
thumbImageView Image URLs to
the
photo URLs parsed from the HTTP response
.
The photos are then fetched and displayed in Stage as a
grid. The
imagethumburl ,
imageurl are used
to
retrieve and
then display photos. The
ImageView
class knows how to
retrieve the image using the URL and display it. Once the photo is
retrieved,
it is displayed in
Scene of
Stage through the use
of data binding.
If the user clicks a thumb photo, the normal photo is loaded and
shown.

When the user clicks a thumb photo, the
ThumbImageView onMouseClicked
function sets the fullImageView url to the clicked photo's
larger image url and sets the fullImageView show variable to true.
Code Sample from: main.fx |
// Initialize fullscreen ImageView
var fullImageView = FullImageView {
translateX: thumbBaseX
translateY: thumbBaseY
visible: false
}
// To display thumb image
class ThumbImageView extends ImageView {
public override var onMouseClicked =
function(e:MouseEvent) {
// Load larger image
fullImageView.image = Image {
url: "{photo.imageurl}"
placeholder: image
backgroundLoading: true
};
}
fullImageView.show = true;
}
|
When the fullImageView show variable is set to true,
The
FullImageView Node's
visible variable
is set to
true which
specifies that this
Node and any subnodes should be rendered as part of the
scene graph. The
fader() function causes the
fullImageView
to fade in or out. The
FullImageView Node's opacity variable
specifies how opaque (that is, solid) the Node appears. Animation
occurs along a timeline, represented by a
javafx.animation.Timeline object. Each timeline contains one or more
key frames, represented by javafx.animation.KeyFrame objects. The value
of the
time instance variable,
3s,
defines the elapsed time at which the values within the key frame will
be set in a single cycle of the
Timeline object. The
play()
method plays the timeline as defined.
Code Sample from: FullImageView.fx |
public class FullImageView extends ImageView {
public var show = false on replace {
if(useEffects) {
fader();
} else {
visible = show;
}
}
var timeline:Timeline = Timeline {
rate: bind timelineRate with
inverse
keyFrames: [
KeyFrame {
time: 3s
values: [ opacity
=> 1.0 tween Interpolator.LINEAR ]
}
]
};
function fader() {
if(show) {
timeline.time = 0s;
timelineRate = 1.0;
opacity
= 0.0;
visible = true;
} else {
timeline.time = 3s;
timelineRate = -3.0;
opacity = 1.0;
}
timeline.play();
}
|
Clicking the normal photo again restores the thumb photo grid. The user
can
navigate to the next or previous set of photos by clicking << and
>> arrow buttons.
Clicking the nextButton calls the onNext() function which increments
the page number and calls
loadImageMetadata();
Code Sample from: main.fx |
// Display next set of photos
var nextButton = ImageButton {
selectImage: Image {
url:
"{__DIR__}images/next_h.png"
};
onMouseClicked: function(e) {
onNext();
}
}
// Load image and data specified in given Photo object
function onNext() {
fullImageView.show = false;
page++;
//updateImages();
loadImageMetadata();
}
|
Running the code
Netbeans
6.5 has plugins for JavaFX. If you don't have Netbeans
6.5
download
and install it, then go to Tools..Plugins and select the JavaFX
plugins. You can also
download the JavaFX SDK
without Netbeans, but I'm going to focus on using JavaFX with Netbeans.
To start learning JavaFX you can start off with the doc
Creating
Your First JavaFX Application and/or with the tutorial
Learning
the JavaFX Script Programming Language.
Running the RESTful Catalog service
- Download the sample
code and extract its contents. You should now see the newly
extracted directory
as
<sample_install_dir>/catalog, where <sample_install_dir>
is the directory
where you unzipped the sample package. For example, if you extracted
the contents to C:\ on a Windows machine,
then your newly created directory should be at C:\catalog.
- Start NetBeans IDE. Click Open Project in the File menu and
select the
catalog directory you just
unzipped.
- Start the MySQL or JavaDB database as follows:
- Click the Services tab in the NetBeans IDE.
- Expand the databases node. You should see the MySQL server
or JavaDB database in the list of databases.
- Right-mouse click on the MySQL or JavaDB server database and
select
Start.
- Create the catalog database as follows:
- Right-mouse click on the MySQL or JavaDB server database and
select
Create Database.
- Enter the database name petcatalog and userid root and
password admin. This will open a New
Database Connection window. Click O.K. to accept the displayed
settings.
- Create the tables in the MySQL catalog database as follows:
- Expand the Drivers node. You should see a driver for the
petcatalog
database in the list of drivers.
- Right-mouse click on the petcatalog driver and select Connect.
- Right-mouse click on the petcatalog driver and select Execute
Command. This will open up a SQL command window.
- Copy the contents of the
catalog.sql file
in the catalog directory and paste the contents into the
SQL command window.
- Click the Run SQL icon
(Ctrl+Shift+E) above the SQL command window.
- Open the
catalog/setup/sun-resources.xml file and verify that the property
values it specifies match those of the database you created (jndi
datasource, username, password...). For example
<resources>
<jdbc-resource jndi-name="catalog"
/>
<property name="User" value="root"/>
<property name="Password" value="admin"/>
<property name="serverName" value="localhost"/>
<property name="portNumber" value="3306"/>
<property name="databaseName"
value="petcatalog"/>
<property name="URL"
value="jdbc:mysql://localhost:3306/petcatalog"/>
</resources>
Edit the property values as
necessary to match the database you
created .
- Build the project as follows:
- Right click the
catalog node in
the
Projects window.
- Select Clean and Build Project.
- Run the project as follows:
- Right click the
catalog node in
the
Projects window.
- Select Run Project.
When you run the project, your browser should display and empty html
page at
http://localhost:8080/catalog/ (this is
the service not the client).
Running the JavaFX Pet Catalog Client
- Download the sample
code and extract its contents. You should now see the newly
extracted directory
as
<sample_install_dir>/catalog, where <sample_install_dir>
is the directory
where you unzipped the sample package. For example, if you extracted
the contents to C:\ on a Windows machine,
then your newly created directory should be at C:\catalogclient.
- In NetBeans IDE, click Open Project in the File menu and
select the
catalogclient directory you just
unzipped.
- Run the project as follows:
- Right click the
catalogclient node in
the
Projects window.
- Select Run Project.
When you run the project, your browser should display the Pet Catalog
Client.
References