Over a million developers have joined DZone.

Registering Multiple Actions (or Handlers) in JavaFX

· Java Zone

Learn more about Kotlin, a new programming language designed to solve problems that software developers face every day brought to you in partnership with JetBrains.

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
tict
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:

  • It defines an instance variable called duration, which enables the user to specify the resolution of a clock tick at object instantiation.
  • It defines two additional public functions called registerHandler() and unRegisterHandler() which take a function pointer (a handler) as an argument.  By registering a handler, the function will be included in the list of handlers to be executed each time the specified duration expires.
  • A handler is registered by inserting it's function pointer argument into an internal sequence of function pointers called handlers[].
  • A handler is similarly unregistered by deleting it's function pointer argument from the handlers[] sequence.
  • The action instance variable, which is part of the KeyFrame instance, now calls an internal function called runHandlers()runHandlers() sequentially executes the functions found in the handlers[] sequence.
Here's the new class:
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.

From http://blogs.sun.com/jtc

The Java Zone is brought to you in partnership with JetBrains.  Discover how powerful static code analysis and ergonomic design make development not only productive but also an enjoyable experience.

Topics:

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}