Java developers, especially those performing any type of GUI
work, will ultimately encounter Java's event-driven programming
paradigm. In short, if programmers want to act upon some
kind of event they bundle up a chunk of code into a Java method,
typically referred to as a handler, and register the handler with
that event. Whenever that event occurs, the handler code
will automatically be executed.
JavaFX provides a similar mechanism. For a straightforward example, the code below defines a simple timer in JavaFX with a resolution of 1 second. Each time a second expires, the function specified by the action instance variable will be executed. Here's what it looks like:
import javafx.animation.*;
public class SimpleTimer {
public def timeline = Timeline {
repeatCount: 5
interpolate: false
keyFrames: [
KeyFrame {
time: 1s
action: function () : Void {
println("tick");
}
}
]
}
}
Adding a run() function, as follows, to the bottom of this source will enable you run an instance of this timer:
function run() : Void {
var s = SimpleTimer{};
s.timeline.playFromStart();
}
The output from this run looks like this:
tick tick tick tick tick
It's all well and good if you only need a single action. What if you wanted to perform multiple actions and/or dynamically add or subtract a number of actions? We can enhance our previous SimpleTimer class to dynamically register and unregister handlers by taking advantage of two of JavaFX's features: sequences and function pointers.
Our new class provides more flexibility:
import javafx.animation.*;
public class Timer {
/**
* User-definable: specifies the length of time for one cycle.
*/
public var duration = 100ms;
public def timeline = Timeline {
repeatCount: Timeline.INDEFINITE
interpolate: false
keyFrames: [
KeyFrame {
time: duration
action: runHandlers
}
]
}
// Holds the list of handlers to run
protected var handlers: function() [];
/**
* Add the function, represented by the handler argument, to the list
* of handlers. These will run when the elapsed time, specified
* by the duration instance variable, expires.
*/
public function registerHandler(handler : function()) : Void {
for (func in handlers) {
if (handler == func) {
return; // handler already registered -- skip
}
}
insert handler into handlers;
}
/**
* Remove the function, represented by the handler argument, from
* the list of handlers.
*/
public function unRegisterHandler(handler : function()) : Void {
delete handler from handlers;
}
protected function runHandlers() : Void {
for (handler in handlers) {
handler();
}
}
}
To test this class out, we'll add a run() function at the script level. The run() function creates a Timer instance and registers two handler functions, decrementTenthsRemaining() and processTicks(). Here's the code:
function run() : Void {
var t = Timer {};
var tenthsRemaining = 100;
var decrementTenthsRemaining = function() : Void {
tenthsRemaining -= 1;
}
var processTick = function() : Void {
if (tenthsRemaining mod 10 == 0) {
println("seconds left: {tenthsRemaining / 10}");
}
if (tenthsRemaining == 0) {
t.timeline.stop();
}
};
t.registerHandler(decrementTenthsRemaining);
t.registerHandler(processTick);
t.timeline.play();
}
And this is the output from the run:
seconds left: 9 seconds left: 8 seconds left: 7 seconds left: 6 seconds left: 5 seconds left: 4 seconds left: 3 seconds left: 2 seconds left: 1 seconds left: 0
Shameless Promotion: Keep up to date with the latest
status of our upcoming JavaFX Book entitled
JavaFX: Developing Rich Internet Applications
at jfxbook.com.