the complete webmaster

Clocks

Introduction

Ever think of creating a clock of your own style? Java has provided you enough tools to make your life easier. Beyond standard clock functions, they can be used in a variety of ways. For example, use them as timing devices for scheduled jobs, i.e. telling the system when to do what. Imagine this: you used to read the news at CNN's website 8:00 in the morning. What if your clock automatically opens that URL for you at that specific time? Isn't that just great? Besides, understanding the clock code in Java is far from difficult. To make it even simpler, two examples are provided here and explained in detail. Hopefully, you can get the hang of it in no time.

I am going to walk through the codes of the two styles of clocks - digital and analog. After you understand how they work, you should be able to create your own version of clocks. Some similar clock references can be found at lots of websites. Check out Java Review Service (www.jars.com), Developer's Daily (www.devdaily.com), etc. All the demo files are downloadable: dc.html, dc.java, dc.class, ac.html, ac.java, and ac.class.

Basics

I have mentioned double buffering a few times but would like to reiterate its importance in animation here. Double buffering is used to prepare a duplicate off-screen graphics object, which mainly shows the change from the previous frame and is drawn only when it is completely ready. This is done in order to reduce screen flickering caused by drawing and displaying at the same time. Also, in the analog clock I will present below, in order to decide the x coordinate for a point on a circle, it is simply radius times cosine plus the translation from the circle center. Similarly for y. If you don't know what in the world I am talking about. Don't worry. Let's just leave it at that for now. :-)

A Digital Clock

The source code of the digital clock is listed in Fig 1 and its corresponding HTML is in Fig 2. Lines 1 - 3 import classes from JDK. Line 7 is for the offscreen graphics and Line 8 is for the offscreen image. Line 9 is for the thread control. Line 10 is for the panel size and animation delay. Line 12 is for the time object containing all info. Line 13 is for the time string. timewidth, timeheight, and timedescent are for the font used to display time. timeH, timeM, and timeS are used to store hour, minute, and second respectively. Lines 19 - 21 are to set up the panel size. Line 23 creates an object for offscreen image. Line 24 creates an object for offscreen graphics. In start(), we start a new thread if no thread is running. In stop(), we stop a running thread. We control the refreshing speed by delay in lines 44 - 58. Lines 61 - 64 override the default update() method. Lines 68 - 71 draw the background and set up font and color. Lines 72 - 75 retrieve the current time and store it into variables. Lines 76 - 88 construct the time string. Lines 90 - 97 get the font measures and draw the time string and its shadow. Line 99 refreshes the screen with the offscreen image.

001 import java.awt.*;
002 import java.applet.*;
003 import java.util.Date;
004
005 public class dc extends Applet implements Runnable
006    {
007    Graphics og;
008    Image oi;
009    Thread t = null;
010    int width, height, delay = 500;
011
012    Date currTime;
013    String time;
014    int timewidth, timeheight, timedescent, timeH, timeM, timeS;
015
016   public void init()
017      {
018      super.init();
019      width = size().width;
020      height = size().height;
021      resize(width, height);
022
023      oi = createImage(width, height);
024      og = oi.getGraphics();
025      }
026
027   public void start()
028      {
029      if (t == null)
030         {
031         t = new Thread(this);
032         t.start();
033         }
034      }
035
036    public void stop()
037       {
038       t.stop();
039       t = null;
040       }
041
042    public void run()
043       {
044       Thread.currentThread().setPriority(Thread.NORM_PRIORITY - 1);
045       while (true)
046          {
047          repaint();
048          try
049            {
050            Thread.sleep(delay);
056            }
057          catch (InterruptedException e) {}
058          }
059       }
060
061    public void update(Graphics g)
062       {
063       paint(g);
064       }
065
066    public void paint(Graphics g)
067       {
068       og.setColor(new Color(170, 170, 170));
069       og.fillRect(0, 0, width, height);
070       og.setFont(new Font("Helvetica", Font.BOLD, 15));
071       og.setColor(new Color(20, 20, 20));
072       currTime = new Date();
073       timeH = currTime.getHours();
074       timeM = currTime.getMinutes();
075       timeS = currTime.getSeconds();
076       time = "";
077       if (timeH < 10)
078          time = "0" + timeH;
079       else
080          time = "" + timeH;
081       if (timeM < 10)
082          time += ":0" + timeM;
083       else
084          time += ":" + timeM;
085       if (timeS < 10)
086          time += ":0" + timeS;
087       else
088          time += ":" + timeS;
089
090       timewidth = (og.getFontMetrics()).stringWidth(time);
091       timeheight = (og.getFontMetrics()).getHeight();
092       timedescent = (og.getFontMetrics()).getDescent();
093       og.drawString(time, (width - timewidth) / 2, height -
094          (height - timeheight) / 2 - timedescent);
095       og.setColor(new Color(230, 230, 230));
096       og.drawString(time, (width - timewidth) / 2 - 1, height -
097          (height - timeheight) / 2 - timedescent - 1);
098
099       g.drawImage(oi, 0, 0, this);
100       }
101    }

Fig 1: source code for the digital clock
< applet code=scroller.class width=70 height=30>
</applet>

Fig 2: HTML example for the digital clock

An Analog Clock

The source code of the analog clock is listed in Fig 3 and its corresponding HTML is in Fig 4. Lines 1 - 3 import classes from JDK. Line 7 is for the offscreen graphics and Line 8 is for the offscreen image. Line 9 is for the thread control. Line 10 is for the panel size and animation delay. Line 12 is for the time object containing all info. In line 13, timeH, timeM, and timeS are used to store hour, minute, and second respectively. radius is for the actual clock size. cx and cy are clock center coordinates. lenH, lenM, and lenS are lengths of the displaying hands. Lines 19 - 21 are to set up the panel size. Lines 23 - 28 are used to set up lengths of the hands and clock center. Line 30 creates an object for offscreen image. Line 31 creates an object for offscreen graphics. In start(), we start a new thread if no thread is running. In stop(), we stop a running thread. We control the refreshing speed by delay in lines 51 - 60. Lines 63 - 66 override the default update() method. Lines 70 - 71 draw the background. Lines 72 - 75 retrieve the current time and store it into variables. Lines 77 - 78 corrects the time to 12-hour mode. Lines 80 - 98 draw the 12 number markers and their shadows. Lines 99 - 106 draw the hour hand and its shadow. Lines 108 - 113 draw the minute hand and its shadow. Lines 115 - 120 draw the second hand and its shadow. Lines 122 - 127 draw the clock boundary and its shadow. Line 129 refreshes the screen with the offscreen image.

001 import java.awt.*;
002 import java.applet.*;
003 import java.util.Date;
004
005 public class ac extends Applet implements Runnable
006    {
007    Graphics og;
008    Image oi;
009    Thread t = null;
010    int width, height, delay = 500;
011
012    Date currTime;
013    int timeH, timeM, timeS, radius = 50, lenH, lenM, lenS,
014        lenIn, cx, cy, x1, y1, x2, y2;
015
016    public void init()
017       {
018       super.init();
019       width = size().width;
020       height = size().height;
021       resize(width, height);
022
023       lenH = 5 * radius / 10;
024       lenM = 6 * radius / 10;
025       lenS = 7 * radius / 10;
026       lenIn = 8 * radius / 10;
027       cx = width / 2;
028       cy = height / 2;
029
030       oi = createImage(width, height);
031       og = oi.getGraphics();
032       }
033
034    public void start()
035       {
036       if (t == null)
037          {
038          t = new Thread(this);
039          t.start();
040          }
041       }
042 
043    public void stop()
044       {
045       t.stop();
046       t = null;
047       }
048
049    public void run()
050       {
051       Thread.currentThread().setPriority(Thread.NORM_PRIORITY - 1);
052       while (true)
053          {
054          repaint();
055          try
056             {
057             Thread.sleep(delay);
058             }
059          catch (InterruptedException e) {}
060          }
061       }
062
063    public void update(Graphics g)
064       {
065       paint(g);
066       }
067
068    public void paint(Graphics g)
069       {
070       og.setColor(new Color(170, 170, 170));
071       og.fillRect(0, 0, width, height);
072       currTime = new Date();
073       timeH = currTime.getHours();
074       timeM = currTime.getMinutes();
075       timeS = currTime.getSeconds();
076
077       if (timeH >= 12)
078          timeH -= 12;
079
080       for (int i = 1; i < 13; i++)
081          {
082          og.setColor(new Color(20, 20, 20));
083          x2 = (int)(cx + radius * Math.sin(i * 2 * 3.14159f / 12));
084          y2 = (int)(cy - radius * Math.cos(i * 2 * 3.14159f / 12));
085          if (i % 3 != 0)
086             {
087             x1 = (int)(cx + 0.9f * radius * Math.sin(i * 2 * 3.14159f / 12));
088             y1 = (int)(cy - 0.9f * radius * Math.cos(i * 2 * 3.14159f / 12));
089             }
090          else
091             {
092             x1 = (int)(cx + 0.8f * radius * Math.sin(i * 2 * 3.14159f / 12));
093             y1 = (int)(cy - 0.8f * radius * Math.cos(i * 2 * 3.14159f / 12));
094             }
095          og.drawLine(x1, y1, x2, y2);
096          og.setColor(new Color(230, 230, 230));
097          og.drawLine(x1 - 1, y1 - 1, x2 - 1, y2 - 1);
098          }
099       og.setColor(new Color(20, 20, 20));
100       x2 = (int)(cx + lenH * Math.sin((timeH + timeM / 60.0f + timeS / 3600.0f) 
101            * 2 * 3.14159f / 12));
102       y2 = (int)(cy - lenH * Math.cos((timeH + timeM / 60.0f + timeS / 3600.0f)
103            * 2 * 3.14159f / 12));
104       og.drawLine(cx, cy, x2, y2);
105       og.setColor(Color.red);
106       og.drawLine(cx - 1, cy - 1, x2 - 1, y2 - 1);
107
108       og.setColor(new Color(20, 20, 20));
109       x2 = (int)(cx + lenM * Math.sin((timeM + timeS / 60.0f) * 2 * 3.14159f / 60));
110       y2 = (int)(cy - lenM * Math.cos((timeM + timeS / 60.0f) * 2 * 3.14159f / 60));
111       og.drawLine(cx, cy, x2, y2);
112       og.setColor(Color.green);
113       og.drawLine(cx - 1, cy - 1, x2 - 1, y2 - 1);
114
115       og.setColor(new Color(20, 20, 20));
116       x2 = (int)(cx + lenS * Math.sin(timeS * 2 * 3.14159f / 60));
117       y2 = (int)(cy - lenS * Math.cos(timeS * 2 * 3.14159f / 60));
118       og.drawLine(cx, cy, x2, y2);
119       og.setColor(Color.blue);
120       og.drawLine(cx - 1, cy - 1, x2 - 1, y2 - 1);
121
122       og.setColor(new Color(20, 20, 20));
123       og.drawOval((width - 2 * radius) / 2, (height - 2 * radius) / 2, 2 * radius, 
124          2 * radius);
125       og.setColor(new Color(230, 230, 230));
126       og.drawOval((width - 2 * radius) / 2 - 1, (height - 2 * radius) / 2 - 1,
127          2 * radius, 2 * radius);
128
129       g.drawImage(oi, 0, 0, this);
130       }
131    }

Fig 3: source code for the analog clock
< applet code=ac.class width=120 height=120>
</applet> 

Fig 4: HTML example for the analog clock

Conclusion

The codes for two styles of clocks are explained what-I-believe-to-be in sufficient detail in this article - digital and analog. As I mentioned at the beginning of this article, there are some functionalities beyond clocks' normal usage, e.g. scheduled work. With a deeper understanding of how clocks work, you should be able to create your own artistic clocks and incorporate some jobs that can be tied to a timing device. I hope this article can inspire you in that sense. I will talk about more common dynamic features that are widely seen on the web in the coming series.

NEXT: Menus: Part I

Author: Chunyen Liu
Date: 03/10/99

More articles about Java
More articles by Chunyen Liu
Author Biography

Copyright 1997, 1998 A Big Lime. All rights reserved.