Creating a Digital Clock
- Skill Level Intermediate
- Supported Versions JavaFX 1.3
- Key Features Date/time, image sequences, animation timeline
- Last Updated March 2010
In this tutorial, you will learn how to program the following digital clock:
The logic for this program can be divided into two main sections: nongraphical code (to track the current time), and GUI code (to render that time on screen as LED digits). The following step-by-step instructions contain technical discussions, complete source code listings, and screenshots of the expected output.
1) Create the DigitalClock Project
To create the DigitalClock project within NetBeans IDE:
- Click the new project icon (or choose New Project from the File menu).
- Choose JavaFX from the Category menu.
- Choose JavaFX Script Application from the Projects menu.
- Click Next.
- Type
DigitalClockas the project name. - Click Finish.
These steps create the Main.fx source file in the
digitalclock package. Delete any autogenerated code, leaving only the package statement at the top of the file:
package digitalclock;
2) Get the Current Time
To get the current time, import the
javafx.date.DateTime class, create a DateTime object, then read the value of its instant variable:
package digitalclock;
import javafx.date.DateTime;
def dt = DateTime{}.instant;
println(dt);
A DateTime object represents a specific instant in time, with millisecond precision (for example, 1263764554296). Its instant variable represents the exact time at which the DateTime object was created.
3) Format the Current Time
Next, convert that time into
hh:mm:ss (hour, minute, second) format. The conversion is most easily accomplished by using the SimpleDateFormat class from the Java programming language:
package digitalclock;
import javafx.date.DateTime;
import java.text.SimpleDateFormat;
// Get the current date and time from within JavaFX
def dt = DateTime{}.instant;
println(dt);
// Format date and time with Java
def hh = new SimpleDateFormat("hh").format(dt);
def mm = new SimpleDateFormat("mm").format(dt);
def ss = new SimpleDateFormat("ss").format(dt);
println("hh:{hh} mm:{mm} ss:{ss}");
The output of this program will now be much more readable
(for example, hh:04 mm:52 ss:09). Note that the hour, minute, and second
data have been stored in their own defs for later access.
4) Download the LED Images
The LED digits for this clock are provided as .png images. Save all 11 images in the same directory as your .fx source code.
Using images mimics real-world workflow where a designer would create the graphics, and you (the developer) would focus only on the clock's logic.
5) Create an Image Sequence
Next, import the
Image class and load the image data into a
sequence named images:
package digitalclock;
import javafx.date.DateTime;
import java.text.SimpleDateFormat;
import javafx.scene.image.Image;
// Get the current date and time from within JavaFX
def dt = DateTime{}.instant;
println(dt);
// Format date and time with Java
def hh = new SimpleDateFormat("hh").format(dt);
def mm = new SimpleDateFormat("mm").format(dt);
def ss = new SimpleDateFormat("ss").format(dt);
println("hh:{hh} mm:{mm} ss:{ss}");
// Load 11 images total (nums 0-9 plus dots image)
def images:Image[] = for(i in [0..10]){Image {url: "{__DIR__}{i}.png"};}
As you complete this step, remember that the Image class simply stores the image data. It is not responsible for displaying the images. (To render an image, you would use ImageView, as discussed in Step 6 below.)
The code marked in red can be analyzed as follows:
def images:Image[] = for(i in [0..10]){Image {url: "{__DIR__}{i}.png"};}declares a sequence -- namedimages-- that will hold the individualImageobjects.
def images:Image[] = for(i in [0..10]){Image {url: "{__DIR__}{i}.png"};}creates aforloop that iterates 11 times (10 iterations for the LED digits 0 through 9, and one iteration for the LED colon image).
def images:Image[] = for(i in [0..10]){Image {url: "{__DIR__}{i}.png"};}uses an object literal to create oneImageobject per loop iteration. Because this loop iterates 11 times, a total of 11Imageobjects will be returned.
6) Display Static Images
Now create the main application frame and statically display the LED images. You can accomplish this goal by completing the following tasks:
- Creating the
StageandSceneobjects. - Adding a
Flowobject to theScene'scontent. - Adding
ImageViewobjects to theFlowobject'scontent.
package digitalclock;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Flow;
import javafx.date.DateTime;
import java.text.SimpleDateFormat;
// Get the current date and time from within JavaFX
def dt = DateTime{}.instant;
// Format date and time with Java
def hh = new SimpleDateFormat("hh").format(dt);
def mm = new SimpleDateFormat("mm").format(dt);
def ss = new SimpleDateFormat("ss").format(dt);
// Load 11 images total (nums 0-9 plus dots image)
def images:Image[] = for(i in [0..10]){Image {url: "{__DIR__}{i}.png"};}
Stage {
title: "Digital Clock"
scene: Scene {
fill: Color.BLACK
width: 374
height: 93
content: Flow {
hgap: 8
content:
[ImageView{image: images[0]}, ImageView{image: images[0]},
ImageView{image: images[10]}, // LED dots
ImageView{image: images[0]}, ImageView{image: images[0]},
ImageView{image: images[10]}, // LED dots
ImageView{image: images[0]}, ImageView{image: images[0]}]
}
}
}
Adding the Stage and Scene is standard practice--something you will do in every GUI program. The Flow layout ensures that GUI objects will be arranged from left to right as they are added to the scene's content. Within that Flow object, there is another content, which is where the LEDs (instances of the ImageView class) are added. The Flow object's hgap variable controls the spacing between the images (8 pixels in this case).
The output minus window decorations should look like the following:
7) Set the Current Time
By now you have a program that determines the current time and draws static LED images to the screen. The next step is to hook everything together to make the LEDs reflect the current time:
package digitalclock;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Flow;
import javafx.date.DateTime;
import java.text.SimpleDateFormat;
def images: Image[] = for (i in [0..10]) {
Image {url: "{__DIR__}{i}.png"};
}
/* This image sequence is the binding target.
* Assigning to this sequence will change
* the time shown on the clock.
*/
var currImgs: Image[];
updateClock();
insert images[10] into currImgs;
Stage {
title: "Digital Clock"
scene: Scene {
fill: Color.BLACK
width: 374
height: 93
content: Flow {
hgap: 8
content: [ImageView {image: bind currImgs[0]},
ImageView {image: bind currImgs[1]},
ImageView {image: bind currImgs[6]}, // LED dots
ImageView {image: bind currImgs[2]},
ImageView {image: bind currImgs[3]},
ImageView {image: bind currImgs[6]}, // LED dots
ImageView {image: bind currImgs[4]},
ImageView {image: bind currImgs[5]}]
}
}
}
function updateClock() {
// Get the current date and time from within JavaFX
var dt = DateTime{}.instant;
/*
* Get the two-digit hour, minute, and second as a String.
* For this, we will use Java's SimpleDateFormat class.
*/
def hh = new SimpleDateFormat("hh").format(dt);
def mm = new SimpleDateFormat("mm").format(dt);
def ss = new SimpleDateFormat("ss").format(dt);
// Finally, map strings to images
currImgs[0] = images[Integer.parseInt(hh.substring(0, 1))];
currImgs[1] = images[Integer.parseInt(hh.substring(1))];
currImgs[2] = images[Integer.parseInt(mm.substring(0, 1))];
currImgs[3] = images[Integer.parseInt(mm.substring(1))];
currImgs[4] = images[Integer.parseInt(ss.substring(0, 1))];
currImgs[5] = images[Integer.parseInt(ss.substring(1))];
}
In this step, a new Image sequence named currImgs has been added. This sequence holds the images that will appear on screen at any given time.
Unlike the previous images sequence (a def sequence
containing all image data for the entire program), this sequence is declared as a var so that it can be freely assigned to.
By binding the Scene's ImageView objects to the images of the currImgs sequence, the GUI will now automatically update itself after each assignment. For example, the code currImgs[0] = images[0] would set the GUI's first LED digit to the number 0. Recall that the current time is already stored in the script as two-digit String objects (variables hh, mm, and ss). By converting those values from String to Integer, you can obtain the appropriate index value to make the clock display the correct time.
Note that in this version the code for obtaining the time has been moved to a separate script-level updateClock() function.
8) Create an Animation Timeline
Finally, you need to create an animation timeline that invokes the updateClock() function every second. Use the javafx.animation.Timeline class to create an infinite timeline that invokes updateClock() at one-second intervals:
package digitalclock;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Flow;
import javafx.date.DateTime;
import java.text.SimpleDateFormat;
import javafx.animation.Timeline;
import javafx.animation.KeyFrame;
def images: Image[] = for (i in [0..10]) {
Image {url: "{__DIR__}{i}.png"};
}
/* This image sequence is the binding target.
* Assigning to this sequence will change
* the time shown on the clock.
*/
var currImgs: Image[];
updateClock();
insert images[10] into currImgs;
// Start the animation timeline, updating the clock every 1s
Timeline{
repeatCount: Timeline.INDEFINITE
keyFrames: KeyFrame {
time: 1s
action: function(){
updateClock();
}
}
}.play();
Stage {
title: "Digital Clock"
scene: Scene {
fill: Color.BLACK
width: 374
height: 93
content: Flow {
hgap: 8
content: [ImageView {image: bind currImgs[0]},
ImageView {image: bind currImgs[1]},
ImageView {image: bind currImgs[6]}, // LED dots
ImageView {image: bind currImgs[2]},
ImageView {image: bind currImgs[3]},
ImageView {image: bind currImgs[6]}, // LED dots
ImageView {image: bind currImgs[4]},
ImageView {image: bind currImgs[5]}]
}
}
}
function updateClock() {
// Get the current date and time from within JavaFX
var dt = DateTime {}.instant;
/*
* Get the two-digit hour, minute, and second as a String.
* For this, we will use Java's SimpleDateFormat class.
*/
def hh = new SimpleDateFormat("hh").format(dt);
def mm = new SimpleDateFormat("mm").format(dt);
def ss = new SimpleDateFormat("ss").format(dt);
// Finally, map strings to images
currImgs[0] = images[Integer.parseInt(hh.substring(0, 1))];
currImgs[1] = images[Integer.parseInt(hh.substring(1))];
currImgs[2] = images[Integer.parseInt(mm.substring(0, 1))];
currImgs[3] = images[Integer.parseInt(mm.substring(1))];
currImgs[4] = images[Integer.parseInt(ss.substring(0, 1))];
currImgs[5] = images[Integer.parseInt(ss.substring(1))];
}
The LED clock application is now complete. You can replace the LED digits with images of your own design to change its look and feel.
Scott Hommel
Senior Technical Writer, Oracle Corporation