Building a Stop Watch Widget

By Jasper Potts, September 24, 2008

This widget emulates a standard stopwatch and is a good demonstration of a multi-file widget application. This example will show you the format in creating a widget class and then placing it neatly on the JavaFX stage for viewing.

StopwatchModel.fx

StopwatchModel Class

This is the class declaration for the stopwatch. Using attributes (equivalent of class variables in traditional java) and class functions, the StopwatchModel is where we declare the time, the hand angles, as well as functions of what start, stop, and reset. This part of the code most resembles what you would see in java

The following snippet of code is the class attribute timeListener that is used to bind together the time to the hand-angles of the stopwatch as well as the String that is displayed at the bottom

Source Code
private attribute timerListener:ActionListener = ActionListener {
    public function actionPerformed(evt:ActionEvent): Void {
        if (lastClockTime == 0) lastClockTime = System.currentTimeMillis() as Integer;
        var now:Integer = System.currentTimeMillis() as Integer;
        var delta = now - lastClockTime;
        elapsedMillis += delta;
        var elapsedHundredthsSecond:Integer = elapsedMillis/10;

        var hundredthsExact:Number = (elapsedMillis/10.0)%10;
        var tenthsExact:Number = (elapsedMillis/100.0)%100;

        var tenths:Integer = (elapsedHundredthsSecond/10)%10;
        var seconds:Integer = (elapsedHundredthsSecond/100)%60;

        handAngle = 180 + ((360/60.0)*seconds);
        tenthsHandAngle = 180 + ((360/10.0)*tenthsExact);
        minutesHandAngle = 180 + ((360/600.0)*seconds);

        var decimalSeconds:Number = (elapsedHundredthsSecond/100.0)%60.0;
        var mins:Integer = elapsedHundredthsSecond/6000;

        timeString = "{%02d mins}:{%05.2f decimalSeconds}";
        
        lastClockTime = now;
    }
}

LightTheme.fx

LightTheme Class

This file creates the graphics of the stopwatch. The graphical content itself is a Group Node that has children Group Nodes within, each rendering separate parts of the stopwatch image(Main Dial, Digits, Start and Stop). Each parent Group has it's own transform coordinates, allowing the developer to create each object separately and then move the completed images together to the desired location in the graphic. The image file stopwatch.png only gives the background of the stopwatch, while the hands, digits, ticks, and mini-dials are all created using JavaFX geometries, and grouped together to create the layout of the stopwatch. Running, the application should look like this:

An important feature of JavaFX is the bind tool - it allows one variable to be automatically updated whenever something it is binded to changes. In the StopwatchModel, the handAngle, tenthsHandAngle, and minutesHandAngle were calculated. In LightTheme, we use the "bind" tool to make sure that whenever those angles are updated, the new image will show the minuteHand, secondHand, or tenthsHand automatically change to its new position, using the "rotate" attribute.

Source Code
// Hand
Group {
    content: [
        Group {
            content: [
                Circle {centerX: 140 centerY: 140 radius: 8
                    fill: Color.web("#FF0000")},
                Rectangle{x: -1.5 y: -20 width: 3 height: 120
                        fill: Color.web("#FF0000")
                        rotate: bind model.handAngle
                        translateX: 140
                        translateY: 140},

            ]
            effect: Lighting{
                light: DistantLight {azimuth: 225}
            }
        },
                Rectangle{x: -1.5 y: -40 width: 3 height: 20
                        fill: Color.web("#FFFFFF")
                        rotate: bind model.handAngle
                        translateX: 140
                        translateY: 140}
    ]
    effect: DropShadow {offsetX: 4 offsetY: 4 radius: 6 color: Color.web("#000000")}
}

StopwatchWidget.fx and Main.fx

Initializing the widget and placing it on the Frame Stage

We've already created the classes that define the Stopwatch and the Stopwatch Graphic. The last thing to do is to create an instance of stopwatch, and place it on the Frame's stage. In Main.fx, we simply create the frame, and under the frame's stage attribute, place our stopwatch Widget. JavaFX will create a window for you and put your finished widget inside.

Source Code
Stage {
    var sw = StopwatchWidget{}
    scene: Scene {
        content: sw
        fill: null // make the background transparent
    }
}

The file StopwatchWidget.fx abstracts the stage's content from our Stopwatch Object. If we don't want to alter the Stopwatch's class code, but wish to add neat effects, this would be the place to do it. Here we can code effects that will alter our viewing of the widget, without losing the original widget, and if we created another widget to go alongside Stopwatch, we could create a switching mechanism that allowed us to go back and forth between the two widgets.