|
JavaFX Examples
Revised date: 2009/02/11
Pub date: 2008/12/28
Author: Terra
Contents
1.Web Applications
1.1 Communication
with Servlet
1.2 Remote
Monitor Servlet
2.Clocks
2.1
Alarm Clock
3.Games
3.1 Air Hockey
1.Web Applications
1.1 Communication with Servlet
This simple JavaFX client communicates
with a Servlet.
The client send a name string entered
in a SwingTextField to the Servlet by invoking a
HTTP Request GET method. When a response is gotten
from the Servlet, it is displayed as a Text.
・JavaFX Ver.: JavaFX 1.0

・JavaFX Client Source Code
/*
*
HelloClient.fx
*
*
Created on 2009/02/08, 17:46:43
*/
package
myclient;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.StringBuffer;
import javafx.ext.swing.SwingButton;
import javafx.ext.swing.SwingTextField;
import javafx.io.http.HttpRequest;
//import
javafx.scene.control.TextBox
import javafx.scene.Scene;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
/**
*
@author terra
*/
var name:
String;
def request:
HttpRequest = HttpRequest {
method:
HttpRequest.GET
onInput:
function(input: java.io.InputStream) {
var
buff: StringBuffer = new StringBuffer();
var
reader: BufferedReader
=
new BufferedReader(new InputStreamReader(input));
var
data: String;
while
((data = reader.readLine()) != null) {
buff.append(data);
}
response
= buff.toString();
reader.close();
}
}
var response:
String;
Stage {
title:
"Communication with Servlet"
width:
350, height: 150
scene:
Scene {
content:
[
Text
{
x:
10, y: 30
font:
Font{size: 18}
content:
"Enter your name:"
},
SwingTextField
{
// TextBox
{
translateX:
155, translateY: 10
width:
100
text:
bind name with inverse
},
SwingButton
{
translateX:
270, translateY: 10
text:
"OK"
action:
function() {
request.location
=
"http://localhost:8080/HelloServlet/HelloServlet?name={name}";
request.enqueue();
}
}
Text
{
x:
10, y: 70
font:
Font{size: 24}
content:
bind response
}
]
}
}
・Servlet Source Code
package
myservlet;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
*
@author terra
*/
public class
HelloServlet extends HttpServlet {
protected
void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws
ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter
out = response.getWriter();
try
{
out.println("Hello,
" + request.getParameter("name")
+ "!");
}
finally {
out.close();
}
}
@Override
protected
void doGet(HttpServletRequest request, HttpServletResponse
response)
throws
ServletException, IOException {
processRequest(request,
response);
}
@Override
protected
void doPost(HttpServletRequest request, HttpServletResponse
response)
throws
ServletException, IOException {
processRequest(request,
response);
}
@Override
public
String getServletInfo() {
return
"Short description";
}
}
1.2 Remote
Monitor Servlet
This example showcases how to access
Servlet cyclically from a JavaFX client.
The client simulates the remote
monitor.
・JavaFX Ver.: JavaFX 1.0

・JavaFX Client Source Code
/*
*
MonitorClient.fx
*
*
Created on 2009/02/11, 21:06:43
*/
package
myclient;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.StringBuffer;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.ext.swing.SwingButton;
import javafx.ext.swing.SwingTextField;
import javafx.io.http.HttpRequest;
import javafx.scene.paint.Color;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.Scene;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import javafx.stage.Stage;
/**
*
@author terra
*/
var response:
String = "0";
Timeline
{
repeatCount:
Timeline.INDEFINITE
keyFrames:
KeyFrame {
time:
1s
action:
function(): Void {
request.enqueue();
}
}
}.play();
def request:
HttpRequest = HttpRequest {
location:
"http://localhost:8080/MonitorServlet/MonitorServlet";
method:
HttpRequest.GET
onInput:
function(input: java.io.InputStream) {
var
buff: StringBuffer = new StringBuffer();
var
reader: BufferedReader
=
new BufferedReader(new InputStreamReader(input));
var
data: String;
while
((data = reader.readLine()) != null) {
buff.append(data);
}
response
= buff.toString();
reader.close();
}
}
Stage {
title:
"Remote Monitor"
width:
180, height: 150
scene:
Scene {
content:
[
Rectangle
{
x:
20, y: 20
width:
40, height: 59
fill:
LinearGradient {
startX:
0.0, startY: 0.0,
endX:
1.0, endY: 0.0
proportional:
true
stops:
[
Stop
{offset: 0.0, color: Color.DARKGREEN},
Stop
{offset: 0.3, color: Color.LIMEGREEN},
Stop
{offset: 1.0, color: Color.DARKGREEN}
]
}
},
Rectangle
{
x:
20, y: 20
width:
40
height:
bind 59 - java.lang.Integer.parseInt(response)
fill:
Color.BLACK
},
Text
{
x:
35, y: 100
font:
Font{name: "Monospaced", size: 14}
content:
bind "{%2d java.lang.Integer.parseInt(response)}"
}
]
}
}
・Servlet Source Code
package
myservlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Calendar;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
*
@author terra
*/
public class
MonitorServlet extends HttpServlet {
protected
void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws
ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter
out = response.getWriter();
try
{
Calendar
rightNow = Calendar.getInstance();
out.println(String.valueOf(rightNow.get(Calendar.SECOND)));
}
finally {
out.close();
}
}
@Override
protected
void doGet(HttpServletRequest request, HttpServletResponse
response)
throws
ServletException, IOException {
processRequest(request,
response);
}
@Override
protected
void doPost(HttpServletRequest request, HttpServletResponse
response)
throws
ServletException, IOException {
processRequest(request,
response);
}
@Override
public
String getServletInfo() {
return
"Short description";
}
}
2.Clocks
2.1 Alarm
Clock
This example showcases the following.
- How to extend the
javafx.ext.swing.SwingComponent.
- How to play a sound.
- How to show a message
dialogue.
・JavaFX Ver.: JavaFX 1.0

・Source Code
/*
*
AlarmClock.fx
*
Alarm Clock.
*
*
Created on 2009/01/11, 14:16:15
*/
package
clock;
import java.applet.Applet;
import java.applet.AudioClip;
import java.awt.Dimension;
import java.lang.Math;
import java.net.URL;
import java.util.Date;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.ext.swing.SwingButton;
import javafx.ext.swing.SwingComponent;
import javafx.ext.swing.SwingLabel;
import javafx.scene.Group;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.Scene;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.transform.Rotate;
import javafx.scene.transform.Translate;
import javafx.stage.Stage;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
/**
*
@author terra
*/
var url:
URL = new URL("{__DIR__}audio/TestMusic.wav");
var audioClip:
AudioClip = Applet.newAudioClip(url);
var seconds:
Integer = 0;
var minutes:
Integer = 0;
var hours:
Integer = 0;
var timerValue:
Number = 0;
var startTime:
Number;
var remainingTime:
Number;
var alarmEnabled:
Boolean = false;
var running:
Boolean = false on replace {
if
((alarmEnabled == true) and (remainingTime <=
0)) {
alarmEnabled
= false;
audioClip.loop();
JOptionPane.showMessageDialog(null,
"It's time!", "Alarm",
JOptionPane.WARNING_MESSAGE);
audioClip.stop();
}
}
var timeline:
Timeline = Timeline {
repeatCount:
Timeline.INDEFINITE
keyFrames:
KeyFrame {
time:
1s
action:
function(): Void {
var
now = new Date();
remainingTime
= timerValue - now.getTime() / 1000 + startTime;
seconds
= remainingTime mod 60 as Integer;
minutes
= (remainingTime / 60) mod 60 as Integer;
hours
= (remainingTime / 60) / 60 as Integer;
if
(remainingTime <= 0) {
timeline.stop();
running
= false;
hours
= 0;
minutes
= 0;
seconds
= 0;
}
else {
running
= true;
}
}
}
}
Stage {
title:
"Alarm Clock"
scene:
Scene {
width:
310, height: 125
content:
[
HBox
{
spacing:
20
content:
[
Group
{
content:
[
Circle
{
centerX:
60, centerY: 60
radius:
50
fill:
Color.WHITE,
stroke:
Color.BLACK
},
Group
{
translateX:
58, translateY: 64
content:
for
(i in [1..12]) Text {
var
radians = Math.toRadians(30 * i - 90)
transforms:
Translate {
x:
40 * Math.cos(radians)
y:
40 * Math.sin(radians)
}
content:
"{i}"
font:
Font {
size:
10
}
}
},
Group
{
translateX:
60, translateY: 60
content:
[
Line
{ // hours hand
endX:
0, endY: -20
strokeWidth:
5
stroke:
Color.BLACK
transforms:
Rotate {
angle:
bind hours * 30 + minutes / 2
}
},
Line
{ // minutes hand
endX:
0, endY: -25
strokeWidth:
2
stroke:
Color.BLUE
transforms:
Rotate {
angle:
bind minutes * 6 + seconds / 10
}
},
Line
{ // seconds hand
endX:
0, endY: -30
strokeWidth:
1
stroke:
Color.RED
transforms:
Rotate {
angle:
bind seconds * 6
}
}
]
},
Circle
{
centerX:
60, centerY: 60
radius:
3
}
]
},
VBox
{
translateY:
10
spacing:
10
content:
[
Group
{
content:
[
IntSpinner
{
minimum:
0
maximum:
11
value:
bind hours with inverse
translateX:
10
},
SwingLabel{
text:
"hour"
translateX:
70, translateY: 5
}
]
},
Group
{
content:
[
IntSpinner
{
minimum:
0
maximum:
59
value:
bind minutes with inverse
translateX:
10
},
SwingLabel{
text:
"min "
translateX:
70, translateY: 5
}
]
},
Group
{
content:
[
IntSpinner
{
minimum:
0
maximum:
59
value:
bind seconds with inverse
translateX:
10
},
SwingLabel{
text:
"sec "
translateX:
70, translateY: 5
}
]
}
]
},
VBox
{
translateX:
10, translateY: 20
spacing:
30
content:
[
SwingButton
{
enabled:
bind not(running)
text:
"Start"
action:
function(): Void {
var
date = new Date();
timerValue
= hours * 3600 + minutes * 60
+
seconds;
startTime
= date.getTime() / 1000;
remainingTime
= timerValue;
if
(timerValue != 0) {
alarmEnabled
= true;
running
= true;
timeline.play();
}
}
},
SwingButton
{
enabled:
bind running
text:
"Stop"
action:
function(): Void {
running
= false;
timeline.stop();
}
}
]
}
]
}
]
}
}
class IntSpinner
extends SwingComponent {
var
spinner: JSpinner;
var
model: SpinnerNumberModel;
public
var minimum: Integer = 0 on replace {
model.setMinimum(minimum);
};
public
var maximum: Integer = 10 on replace {
model.setMaximum(maximum);
};
public
var value: Integer = 3 on replace {
spinner.setValue(value);
};
public
var stepSize: Integer = 1 on replace {
model.setStepSize(stepSize);
};
public
override var width = 50;
public
override var height = 25;
public
override function createJComponent(): JComponent
{
model
= new SpinnerNumberModel();
spinner
= new JSpinner(model);
spinner.setPreferredSize(new
Dimension(width, height));
spinner.addChangeListener(ChangeListener
{
public
override function stateChanged(e: ChangeEvent) {
value
= spinner.getValue() as Integer;
}
});
return
spinner;
}
}
3.Games
3.1 Air Hockey
・JavaFX Ver.: JavaFX 1.0

・How to play
When you run the program, an air
hockey game table with two aqua color mallets are
displayed on your screen. Computer's goal (the upper
one) and your goal (the lower one) are displayed
in burlywood color.
Push the "SET" button
to start the game. Then, a white puck appears on
your side.
You can manipulate your mallet by
the mouse as long as it resides in your area.
A velocity of your mallet is computed
from its displacement by applying the scaling transformation.
The scaling is modulated by the velocityScaling
factor. You can make your mallet's velocity more
real by moving the factor closer to 1.0.
You can also regulate the bounce
back speed by adjusting the elastic modulus elastic.
In this program, a computer simply
move its mallet back and forth. It has no ability
to take the offensive.
・Source Code
/*
*
AirHockey.fx
*
The air hockey game.
*
*
Created and modified:
*
V 1.0.0 2008/09/25 Created.
*
V 2.0.0 2008/12/28 Modified to
conform to JavaFX 1.0.
*/
package
game;
import java.lang.Math;
import javafx.animation.*;
import javafx.scene.*;
import javafx.scene.effect.*;
import javafx.scene.effect.light.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.*;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.*;
import javafx.stage.Stage;
/**
*
@author terra
*/
var goalCorners:
Disc[];
var mouseEvent:
MouseEvent;
var tableWidth:
Number = 300;
var tableHeight:
Number = 400;
var goalWidth:
Number = 80;
var goalCornerRadius:
Number = 5;
var puckRadius:
Number = 10;
var malletRadius:
Number = 15;
var hittingAreaDepth:
Number = tableHeight / 2 - malletRadius;
var elastic:
Number = 0.95;
var velocityScaling
= 0.35;
var malletCMaxV
= 2; // Maximum velocity of the
computer's mallet.
var timingGenerator:
Timeline = Timeline {
repeatCount:
Timeline.INDEFINITE
keyFrames:
KeyFrame {
time:
20ms
action:
function(): Void {
malletC.move();
puck.move();
malletH.calcuVelocity();
puck.collide(malletH);
puck.collide(malletC);
for
(corner in goalCorners) {
puck.collide(corner);
}
score.judgeGoal();
}
}
};
var puck:
Puck = Puck {
x:
-(puckRadius + 1)
y:
goalCornerRadius + tableHeight / 2
radius:
puckRadius
vX:
0, vY: 0
}
var malletH:
MalletH = MalletH { // Human's
mallet.
x:
bind mouseEvent.sceneX
y:
bind mouseEvent.sceneY
radius:
malletRadius
color:
Color.AQUA
}
var malletC:
MalletC = MalletC { // Computer's
mallet.
x:
goalCornerRadius + tableWidth / 2
y:
goalCornerRadius + malletRadius + puckRadius
radius:
malletRadius
color:
Color.AQUA
vX:
malletCMaxV, vY: 0
}
for (i in
[0..1]) {
for
(j in [0..1]) {
insert
Disc {
x:
(tableWidth - goalWidth) / 2 + j * goalWidth
y:
i * (2 * goalCornerRadius + tableHeight)
radius:
goalCornerRadius
color:
Color.LIGHTGREY
}
into goalCorners;
}
}
var score:
Score = Score{}
Stage {
title:
"Air Hockey"
width:
2 * goalCornerRadius + tableWidth + 56 as Integer
height:
2 * goalCornerRadius + tableHeight + 28 as Integer
scene:
Scene {
width:
2 * goalCornerRadius + tableWidth as Integer
height:
2 * goalCornerRadius + tableHeight as Integer
fill:
Color.LIGHTGRAY
content:
[
Rectangle
{ // Jointed goal area.
x:
(tableWidth - goalWidth) / 2, y: 0
width:
goalWidth
height:
2 * goalCornerRadius + tableHeight + 2
fill:
Color.BURLYWOOD
},
Rectangle
{ // Table.
x:
goalCornerRadius, y: goalCornerRadius
width:
tableWidth, height: tableHeight
fill:
Color.OLIVE
},
Rectangle
{ // Human's hittig area.
x:
goalCornerRadius + malletRadius
y:
goalCornerRadius + tableHeight - hittingAreaDepth
width:
tableWidth - 2 * malletRadius
height:
hittingAreaDepth - malletRadius
fill:
Color.OLIVE
onMouseMoved:
function(ev: MouseEvent): Void {
mouseEvent
= ev;
}
},
Line
{ // Center line.
startX:
goalCornerRadius
startY:
goalCornerRadius + tableHeight / 2
endX:
goalCornerRadius + tableWidth - 1
endY:
goalCornerRadius + tableHeight / 2
stroke:
Color.BLUE
},
puck,
malletH,
malletC,
goalCorners,
ScoreIndicator
{
x:
2 * goalCornerRadius + tableWidth + 5 as Integer
y:
goalCornerRadius + tableHeight / 2 - 24 as Integer
score:
bind score.scoreC
},
ScoreIndicator
{
x:
2 * goalCornerRadius + tableWidth + 5 as Integer
y:
goalCornerRadius + tableHeight / 2 + 5 as Integer
score:
bind score.scoreH
},
Rectangle
{ // "SET" button.
x:
2 * goalCornerRadius + tableWidth + 5 as Integer
y:
goalCornerRadius + tableHeight - 30 as Integer
width:
35,
height:
30
arcWidth:
8,
arcHeight:
8
fill:
Color.DARKGREY
effect:
Lighting {
light:
DistantLight {
azimuth:
225,
elevation:
50
}
}
onMousePressed:
function(ev: MouseEvent): Void {
puck.x
= goalCornerRadius + tableWidth / 2;
puck.y
= goalCornerRadius + 2 * tableHeight / 3;
puck.vX
= 0;
puck.vY
= 0;
timingGenerator.play();
}
},
Text
{
x:
2 * goalCornerRadius + tableWidth + 10 as Integer
y:
goalCornerRadius + tableHeight - 20 as Integer
textOrigin:
TextOrigin.TOP
fill:
Color.YELLOW
font:
Font {
size:
14
name:
"Monospaced",
embolden:
true
}
effect:
DropShadow {
offsetX:
3, offsetY: 3
radius:
2
color:
Color.BLACK
}
content:
"SET"
}
]
}
}
class Disc
extends CustomNode {
var
x: Number;
var
y: Number;
var
radius: Number;
var
color: Color = Color.WHITE;
var
vX: Number;
var
vY: Number;
var
lastX: Number = x;
var
lastY: Number = y;
override
function create(): Node {
return
Circle {
centerX:
bind x,
centerY:
bind y
radius:
bind radius
fill:
bind color
};
}
}
class Puck
extends Disc {
function
move(): Void {
x
+= vX;
y
+= vY;
if
(x + radius > goalCornerRadius + tableWidth)
{
x
= goalCornerRadius + tableWidth - radius;
vX
*= -1.0 * elastic;
}
else if (x - radius < goalCornerRadius) {
x
= goalCornerRadius + radius;
vX
*= -1.0 * elastic;
}
if
(x >= 0) {
if
(y > 2 * goalCornerRadius + tableHeight) {
//
It crossed the Human's goal line.
x
= -(radius + 1);
y
= 2 * goalCornerRadius + tableHeight + radius;
vX
= 0;
vY
= 0;
}
else if (y + radius > tableHeight + goalCornerRadius)
{
if
((x < (tableWidth - goalWidth) / 2)
or
(x > (tableWidth + goalWidth) / 2)) {
y
= tableHeight + goalCornerRadius - radius;
vY
*= -1.0 * elastic;
}
}
else if (y < 0) {
//
It crossed the computer's goal line.
x
= -(radius + 1);
y
= -radius;
vX
= 0;
vY
= 0;
}
else if (y - radius < goalCornerRadius) {
if
((x < (tableWidth - goalWidth) / 2)
or
(x > (tableWidth + goalWidth) / 2)) {
y
= goalCornerRadius + radius;
vY
*= -1.0 * elastic;
}
}
}
}
function
collide(disc: Disc): Void {
var
distX: Number = disc.x - x;
var
distY: Number = disc.y - y;
var
minDist: Number = disc.radius + radius;
var
dist2: Number = distX * distX + distY * distY;
var
minDist2: Number = minDist * minDist;
if
(dist2 < minDist2) {
var
colAngle: Number = Math.atan2(distY, distX);
var
sinColAngle: Number = Math.sin(colAngle);
var
cosColAngle: Number = Math.cos(colAngle);
var
expres1: Number = disc.vX * cosColAngle
+
disc.vY * sinColAngle;
var
expres2: Number = vX * sinColAngle - vY * cosColAngle;
var
expres3: Number = vX * cosColAngle + vY * sinColAngle;
x
= disc.x - minDist * cosColAngle;
y
= disc.y - minDist * sinColAngle;
vX
= (1 + elastic) * expres1 * cosColAngle
+
expres2 * sinColAngle - elastic * expres3 * cosColAngle;
vY
= (1 + elastic) * expres1 * sinColAngle
-
expres2 * cosColAngle - elastic * expres3 * sinColAngle;
}
}
}
class MalletH
extends Disc {
function
calcuVelocity(): Void {
vX
= velocityScaling * (x - lastX);
vY
= velocityScaling * (y - lastY);
lastX
= x;
lastY
= y;
}
}
class MalletC
extends Disc {
function
move(): Void {
x
+= vX;
if
(x + radius > 2 * goalCornerRadius + tableWidth
/ 2
+
goalWidth / 2) {
x
= 2 * goalCornerRadius + tableWidth / 2
+
goalWidth / 2- radius;
vX
*= -1.0;
}
else if (x - radius < tableWidth / 2 - goalWidth
/ 2) {
x
= tableWidth / 2 - goalWidth / 2 + radius;
vX
*= -1.0;
}
}
}
class Score
{
var
scoreC: Integer = 0; // Computer's
score.
var
scoreH: Integer = 0; // Human's
score.
function
judgeGoal(): Void {
if
(puck.x < 0) {
if
(puck.y > 2 * goalCornerRadius + tableHeight)
{
scoreC
+= 1;
}
else if (puck.y < 0) {
scoreH
+= 1;
}
puck.y
= goalCornerRadius + tableHeight / 2;
timingGenerator.stop();
}
}
}
class ScoreIndicator
extends CustomNode {
var
score: Integer = 0;
var
x: Integer = 0;
var
y: Integer = 0;
var
width: Integer = 21;
var
height: Integer = 18;
var
fillColor: Color = Color.WHITE;
var
borderColor: Color = Color.YELLOWGREEN;
var
scoreTextColor: Color = Color.BLACK;
override
function create(): Node {
Group
{
content:
[
Rectangle
{
x:
x, y: y
width:
width, height: height
arcWidth:
6, arcHeight: 6
stroke:
borderColor,
fill:
fillColor
},
Text
{
x:
x + 4 * height / 18 as Integer
y:
y + 5 * height / 18 as Integer
textOrigin:
TextOrigin.TOP
fill:
scoreTextColor
font:
Font {
size:
14 * height / 18 as Integer
name:
"Monospaced",
embolden:
true
}
content:
bind "{%2d score}"
}
]
}
}
}
|