Introduction to JavaFX Script
Seite 1, 2
JavaFX code can be easily integrated with Java code. Here is an example that uses JavaFX to load an image into a frame and allows the user to select a rectangular zone and save it. The capture and save operations are accomplished using Java code.
Listing 15
import java.io.*;
import javafx.ui.*;
import javafx.ui.canvas.*;
import javafx.ui.filter.*;
import java.awt.Robot;
import java.awt.Rectangle;
import java.awt.image.RenderedImage;
import javax.imageio.ImageIO;
import java.lang.System;
class CaptureExample extends CompositeNode{
attribute lx: Integer;
attribute ly: Integer;
operation CaptureExample();
}
attribute CaptureExample.lx = 0;
attribute CaptureExample.ly = 0;
operation saveCapture(lx_copy:Integer, ly_copy:Integer) {
var robot = new Robot();
var rect = new Rectangle (lx_copy, ly_copy, 50, 50);
var BI=robot.createScreenCapture(rect);
var file = new File(".//capture.jpg");
ImageIO.write((RenderedImage)BI, "jpg", file);
}
function CaptureExample.composeNode() =
Group{
transform: []
content:[ImageView {
transform: []
image: Image { url: ".//app//Sunset.gif" }
cursor: DEFAULT
onMouseClicked: operation(e:CanvasMouseEvent) {
saveCapture(e.source.XOnScreen,e.source.YOnScreen);
}
onMouseMoved: operation(e:CanvasMouseEvent) {
lx = e.x;
ly = e.y;
}
},
Rect{
x: bind lx
y: bind ly
width: 50
height:50
strokeWidth: 1
stroke: black
}]
};
Frame {
centerOnScreen: true
visible: true
height: 230
width: 300
title: "Capture the screen..."
onClose: operation() {System.exit(0);}
content: ScrollPane {
background: white
view: Canvas {
background: black
cursor: DEFAULT
content: CaptureExample
}
}
}
Notice the use of bind. This is a very important JavaFX operator used for incremental and lazy evaluation of attributes. You can find out more about this operator in the JavaFX Programming Language documentation.
Also, notice that, it is possible to interact in the above application using two mouse events: mouse clicked (onMouseClicked) and mouse moved (onMouseMoved). JavaFX supports the following mouse events:
onMouseClickedonMouseMovedonMousePressedonMouseExitedonMouseEnteredonMouseReleasedonMouseDragged
For asynchronous execution of code you can use the JavaFX do later statement like this:
Listing 16
//asynchronous execution with do later statement
import java.lang.System;
var s1 = "My name is ";
var s2 = "Anghel Leonard";
do later {
System.out.println(s2);
}
System.out.println(s1);
Result: My name is Anghel Leonard
JavaFX allows you to execute a portion of code in a separate thread by placing that code into a do statement. Using this technique the AWT Event Dispach Thread will be able to process all the incoming events. Here is an example that uses an infinite loop into a do statement. Notice that even if you have a infinite loop you still can close the window normally.
Listing 17
import javafx.ui.*;
import java.lang.System;
import javafx.ui.canvas.*;
import java.util.Random;
class DoExample extends CompositeNode{
attribute randomfill: Color;
operation changeOpacity();
}
attribute DoExample.randomfill = Color{red:0 green:0 blue:0};
operation DoExample.changeOpacity() {
do{
while(true)
{
var r = new Random();
var g = r.nextInt(255);
randomfill = Color{red:g green:g blue:g};
}
}
}
function DoExample.composeNode() =
Group {
transform: []
content: [
Text {
x: 20
y: 20
content: "Because of \"do\" you can close this window..."
font: Font {face: VERDANA, style: [ITALIC, BOLD], size: 20}
fill: bind randomfill
opacity: 0.5
onMouseClicked: operation(e:CanvasMouseEvent) {
changeOpacity();
}
}]
};
Frame {
centerOnScreen: true
visible: true
height: 100
width: 580
title: "\"Do\" example..."
onClose: operation() {System.exit(0);}
content: ScrollPane {
background: white
view: Canvas {
background: black
cursor: DEFAULT
content: DoExample
}
}
}
JavaFX code can be integrated with HTML code using the JavaFX Label class. This class supports HTML and CSS in the same manner as a web application. Here is an example that renders an HTML table.
Listing 18
import javafx.ui.*;
import java.lang.System;
import javafx.ui.canvas.*;
class Partners {
attribute name: String;
attribute company: String;
attribute phone: String;
attribute e_mail: String;
attribute partners: Partners*;
}
var myPartners = Partners {
partners: [Partners{
name: "Mary J"
company: "Software ATV Inc."
phone: "0900090345"
e_mail: "maryj@yahoo.com"
},
Partners{
name: "Leona W"
company: "Winkle LTD"
phone: "090849435"
e_mail: "leonaw@yahoo.com"
},
Partners{
name: "Joe T"
company: "Press OJ"
phone: "340909879"
e_mail: "joet@yahoo.com"
}]
};
Frame {
content: Label {
text: bind "<html>
<h2 align='center'>- My Partners -</h2>
<table align='center' border='0' bgcolor='#BBAAEE'>
<tr bgcolor='#FFEE55'>
<td><b>Name</b></td>
<td><b>Company</b></td>
<td><b>Phone</b></td>
<td><b>E-mail</b></td>
</tr>
{
if (sizeof myPartners.partners == 0)
then "<tr bgcolor='#432211'><td colspan='8'><b>
I have no partners...</b></td></tr>"
else foreach (t in myPartners.partners)
"<tr bgcolor='#FF25AD'>
<td>{t.name}</td>
<td>{t.company}</td>
<td>{t.phone}</td>
<td>{t.e_mail}</td>
</tr>"
}
</table>
</html>"
}
visible: true
}

Figure 7. Running Listing 18
In the last section of this article we will see two applications that illustrate how easy it is to build complex animations using JavaFX. The first application simulates an analog clock. The design is based on four images animated with JavaFX code.
Listing 19
import javafx.ui.*;
import javafx.ui.canvas.*;
import javafx.ui.filter.*;
import java.lang.System;
class Clock extends CompositeNode{
attribute seconds: Number+;
attribute minutes: Number+;
attribute hours: Number+;
operation startClock();
}
attribute Clock.seconds = 360;
attribute Clock.minutes = 360;
attribute Clock.hours = 360;
operation Clock.startClock() {
do{
while(true)
{
if(seconds>=360) {seconds = [0,6..360]
dur 60000 linear;}
if(minutes>=360) {minutes = 0; minutes = [0,6..360]
dur 3600000 linear;}
if(hours>=360) {hours = 0;hours = [0,6..360]
dur 43200000 linear;}
}
}
}
function Clock.composeNode() =
Group {
onMouseClicked: operation(e:CanvasMouseEvent) {
startClock();
}
transform: []
content:[ImageView {
image: Image { url: ".//app//clockempty.gif" }
},
ImageView {
transform: bind [translate(203,82),
rotate (seconds,2.5,125)]
image: Image { url: ".//app//l1.gif" }
antialias: true
},
ImageView {
transform: bind [translate(203,100),
rotate (minutes,2.5,107)]
image: Image { url: ".//app//l2.gif" }
antialias: true
},
ImageView {
transform: bind [translate(203,115),
rotate (hours,2.5,92)]
image: Image { url: ".//app//l3.gif" }
antialias: true
},
Circle {
cx: 205
cy: 205
radius: 13
fill: red
strokeWidth: 1
}]
};
Frame {
centerOnScreen: true
visible: true
height: 450
width: 450
title: "JavaFX - Clock"
onClose: operation() {System.exit(0);}
content: ScrollPane {
background: white
view: Canvas {
background: black
cursor: DEFAULT
content: Clock
}
}
}

Figure 8. Running Listing 19
Notice the dur (duration) operator. JavaFX provides this operator for creating animations. It is used to apply an array asynchronously to a time interval. Before returning an element from the array JavaFX will wait a time equal to the specified milliseconds. This process will be repeated until the whole elements are returned. There are four types of interpolations:
lineareaseineaseouteaseboth
By default it is a ease-in, ease-out combination.
The second application simulates a set of pop-up menus that can be dragged on the screen. With a simple click on the menu headers you can hide/show the items using a nice opacity effect. The whole design was written with JavaFX drawing capabilities:
Listing 20
import javafx.ui.*;
import javafx.ui.canvas.*;
import javafx.ui.filter.*;
import java.lang.System;
class MenuOptions extends CompositeNode{
attribute px: Integer;
attribute py: Integer;
attribute lx: Integer;
attribute ly:Integer;
attribute lw:Integer;
attribute itemsOpacity:Number;
attribute menutext: String;
}
trigger on new MenuOptions {
this.px = 0;
this.py = 0;
this.menutext = "";
this.lx = 0;
this.ly = 0;
this.lw = 150;
this.itemsOpacity = 0.0;
}
function MenuOptions.composeNode() =
Group {
transform: bind []
opacity: bind itemsOpacity
content:[Rect {
x: bind lx
y: bind ly
width: lw
height: 20
arcHeight: 10
arcWidth: 10
fill: Color {red:190 green:181 blue:215}
stroke: Color {red:68 green:54 blue:103}
strokeWidth: 2
onMouseEntered: operation(e:CanvasMouseEvent) {
if(itemsOpacity == 0.7) {itemsOpacity = 1.0;}
}
onMouseExited: operation(e:CanvasMouseEvent) {
if(itemsOpacity == 1.0) {itemsOpacity = 0.7;}
}
onMouseClicked: operation(e:CanvasMouseEvent) {
eventListener(this.menutext);
}
},
Text {
x: bind lx+5
y: bind ly+5
content: bind menutext
font: Font {face: VERDANA, style: [BOLD], size: 11}
fill:Color {red:68 green:54 blue:103}
}]
};
class MainMenu extends CompositeNode{
attribute option: String;
attribute px: Integer;
attribute py: Integer;
attribute lx: Integer;
attribute ly:Integer;
attribute lw:Integer;
attribute menutext: String;
attribute step: Integer;
attribute submenu: MenuOptions+;
operation addSubmenu(t:MenuOptions);
operation show_hide();
}
trigger on new MainMenu {
this.option = "";
this.px = 0;
this.py = 0;
this.menutext = "";
this.lx = 0;
this.ly = 0;
this.lw = 150;
this.step = 20;
this.submenu = null;
}
operation MainMenu.addSubmenu(t:MenuOptions) {
t.lx = this.lx;
t.lw = this.lw;
t.ly = this.ly+step;
step=step+20;
insert t into submenu;
}
operation MainMenu.show_hide() {
if(submenu.itemsOpacity[1] == 0.7){submenu.itemsOpacity =
[0.7,0.6,0.5,0.4,0.3,0.2,0.1,0.0] dur 1200;}
else if(submenu.itemsOpacity[1] == 0.0){
submenu.itemsOpacity =
[0.1,0.2,0.3,0.4,0.5,0.6,0.7] dur 1200;}
}
function MainMenu.composeNode() =
Group {
transform: bind []
content:[Rect {
x: bind lx
y: bind ly
height: 20
width: bind lw
arcHeight: 10
arcWidth: 10
fill: Color {red:68 green:54 blue:103}
stroke: Color {red:190 green:181 blue:215}
strokeWidth: 2
onMouseDragged: operation(e:CanvasMouseEvent) {
lx += e.localDragTranslation.x;
ly += e.localDragTranslation.y;
submenu.lx += e.localDragTranslation.x;
submenu.ly += e.localDragTranslation.y;
}
onMouseClicked: operation(e:CanvasMouseEvent) {
show_hide();
}
},
Text {
x: bind lx+5
y: bind ly+5
content: bind menutext
font: Font {face: VERDANA, style: [BOLD], size: 11}
fill:Color {red:190 green:181 blue:215}
},
bind submenu]
};
var menu_1 = new MainMenu();
menu_1.lx = 120;
menu_1.ly = 140;
menu_1.lw = 128;
menu_1.menutext = "Navigate";
var submenu_11 = new MenuOptions();
submenu_11.menutext = "Go to Class...";
var submenu_12 = new MenuOptions();
submenu_12.menutext = "Go to Test";
var submenu_13 = new MenuOptions();
submenu_13.menutext = "Back";
var submenu_14 = new MenuOptions();
submenu_14.menutext = "Forward";
var submenu_15 = new MenuOptions();
submenu_15.menutext = "Go to Line...";
menu_1.addSubmenu(submenu_11);
menu_1.addSubmenu(submenu_12);
menu_1.addSubmenu(submenu_13);
menu_1.addSubmenu(submenu_14);
menu_1.addSubmenu(submenu_15);
var menu_2 = new MainMenu();
menu_2.lx = 260;
menu_2.ly = 140;
menu_2.lw = 90;
menu_2.menutext = "Refactor";
var submenu_21 = new MenuOptions();
submenu_21.menutext = "Rename....";
var submenu_22 = new MenuOptions();
submenu_22.menutext = "Pull Up...";
var submenu_23 = new MenuOptions();
submenu_23.menutext = "Push Down...";
menu_2.addSubmenu(submenu_21);
menu_2.addSubmenu(submenu_22);
menu_2.addSubmenu(submenu_23);
operation eventListener(s:String) {
System.out.println("You choose:{s}");
}
Frame {
centerOnScreen: true
visible: true
height: 500
width: 500
title: "JavaFX - Menu"
onClose: operation() {System.exit(0);}
content: ScrollPane {
background: white
view: Canvas {
background: black
cursor: DEFAULT
content: [menu_1, menu_2]
}
}
}

Figure 9. Running Listing 20
Notice that JavaFX uses triggers (like SQL) instead of constructors. A trigger is marked by the trigger keyword and is made of a header and a body. The header indicates the event that must occur before executing the body content. A trigger can be created, inserted, deleted, or replaced. You can find more details about triggers on the OpenJFX site.
Conclusion
JavaFX Script is a capable new language for the Java platform. With it, you can easily build rich, dynamic interfaces in much less time than you could build something comparable in Java with Swing and Java 2D. In this article, we have stepped through the basic syntax, looked at IDE support, and built a demonstration application that shows off some of JavaFX's Capabilities. JavaFX will quickly become a necessary tool in the Java developer's toolbox.
Resources
- Sample code for this article (for running the examples create a new NetBeans project)
- The offical JavaFX site: from here you can download demos, specifications and plug-ins
- JavaFX classes documentation: get help with JavaFX classes
- Plug-in for NetBeans: get the JavaFX plug-in for NetBeans
- Plug-in for Eclipse: get the JavaFX plug-in for Eclipse
Anghel Leonard is a senior Java developer with more than 12 years of experience, specializing in GIS applications. He has written two books about XML and Java.