A Timer written in JavaFX

I've been playing with JavaFX Script since the announcement at last year's JavaOne and I'm happy to report that my interest in dabbling with JavaFX hasn't waned! The declarative scripting syntax of JavaFX is very appealing to novice/weekend GUI programmers like myself.

For my JavaFX experiment, I needed a text-only display of the current time that would update itself every minute on the minute.

The first part was easy. I created TextClock.fx using my favourite JavaFX IDE - NetBeans. Here's a screenshot of TextClock.fx in action within NetBeans (using the Compiled JavaFX Script plugin).

TextClock.fx screenshot

The above program displays a timestamp but doesn't update it. I needed something that would update timestamp every minute. This is what I came up with.

foobar/util/Timer.fx:

package foobar.util;

import java.util.Calendar;
import javafx.lang.Duration;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;

/\*\*
 \* A Timer class that can be configured/extended to invoke an action after a
 \* specified number of seconds. It can optionally be configured to synchronize
 \* the timer to the system clock.
 \*/
public class Timer {

    // Specifies the interval at which the timer should run
    // (The default interval is 30 seconds)
    public attribute interval: Number = 30;

    // Specifies whether the timer should be synchronized to the system clock.
    // This is useful when creating a timer that fires every minute on the
    // minute, every hour on the hour etc.
    public attribute synchronize: Boolean;

    // Controls whether the timer is running or not. Setting it to true starts
    // the timer, and setting this attribute to false terminates the timer.
    public attribute active:Boolean
    on replace {
        if (initialized) {
            if (active) {
                start()
            } else {
                stop();
            }
        }
    }

    // User configurable action that is invoked when the specified
    // interval expires. This can be used as an alternative to extending the
    // Timer class to implement action2()
    public attribute action: function():Void;

    // Another user configurable action that is invoked when the specified
    // interval expires. This operation can be implemented in the class that
    // extends this Timer class
    public function action2():Void {
        // do nothing in this class
    }

    // Start the timer only after all attributes have been initialized
    postinit {
        initialized = true;
        if (active) {
            start();
        }
    }

    // Prevents the timer from starting before all attributes are set
    private attribute initialized = false;

    // An internal counter used to synchronize this timer with the system clock
    private attribute countdown: Duration;

    // The "main" timer that invokes the user-specified action(s) every time the
    // interval expires
    private attribute tick: Timeline = Timeline {
        repeatCount: Timeline.INDEFINITE
        keyFrames:
        [
        KeyFrame {
            time: Duration {
                millis: bind (interval \* 1000)
            }

            action: function() {
                alarm();
            }
        }]
    };

    // A timer that helps synchronize 'tick' to the next minute
    private attribute sync: Timeline = Timeline {
        repeatCount: 1
        keyFrames:
        [KeyFrame {
            time: bind countdown
            action: function() {
                sync.stop();
                alarm();
                tick.start();
            }
            }]
    };


    // Starts the timer
    private function start():Void {
        var seconds = 0;
        if (synchronize) {
            var d: Calendar = Calendar.getInstance();
            seconds = d.get(Calendar.SECOND);
        }
        if (seconds > 0) {
            countdown = Duration {
                millis: ((60 - seconds) \* 1000)
            };
            sync.start();
        } else {
            alarm();
            tick.start();
        }
    }

    // Stops the timer
    private function stop():Void {
        sync.stop();
        tick.stop();
    }

    // Invokes the user configured action(s)
    private function alarm():Void {
        if (action <> null) {
            (action)();
        }
        action2();
    }
}

The new animation APIs  javafx.animation.Timeline, javafx.animation.KeyFrame were much easier to understand and use than the dur operator that exists in the interpreted version of the language (Previously, I had implemented the Timer class using Interpreted JavaFX Script. That version can be found here).

Hooking up the timer to my clock program was straightforward. The lines in bold below show how I enhanced TextClock.fx to automatically update timestamp every minute.

package foobar.samples;

import java.util.Date;
import java.text.SimpleDateFormat;
import javafx.ui.\*;
import foobar.util.Timer;

var formatter = new java.text.SimpleDateFormat("EEE MMM dd, hh:mm aa");
var now = new java.util.Date();
var timestamp = formatter.<<format>>(now);

var timer = Timer {
    interval: 60        // fire every minute
    synchronize: true
    active: true
    action: function():Void {
        now = new java.util.Date();
        timestamp = formatter.<<format>>(now);
    }
};



Frame {
    visible: true
    title: "Text Clock"   
    width: 200
    content: SimpleLabel { text: bind timestamp }
};

That brings me to the end of my first JavaFX blog! I hope you found it useful.

Attachments:

  1. TextClock.zip (consolidated source files)


Comments:

Post a Comment:
  • HTML Syntax: NOT allowed
About

arvindsrinivasan

Search

Archives
« July 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  
       
Today