# Java: Example - Buffered Analog Clock

| This analog (uses hands) clock uses buffered drawing to increase speed. In buffered drawing some or all of an image is computed once and stored so that it doesn't have to be redrawn later. In this program the clock face is drawn only once, and the hands are computed each time.

## An application/applet which displays the Clock component

      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
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
    // File   : GUI-lowlevel/animation/analogclock/ClockAnalogBuf.java
    // Purpose: An applet/application which displays our clock component.
    // Tag    : <applet code="ClockAnalogBuf.class" height="300" width="300"/>
    // Author : Fred Swartz, 1999-2007, Placed in public domain.

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import javax.swing.event.*;
    import java.util.*;

    ///////////////////////////////////////////////////////////// ClockAnalogBuf
    public class ClockAnalogBuf extends JApplet {

        //=============================================================== fields
        private Clock _clock;                        // Our clock component.

        //================================================================= main
        public static void main(String[] args) {
            JFrame window = new JFrame();
            window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            window.setTitle("Analog Clock");
            window.setContentPane(new ClockAnalogBuf());
            window.pack();                          // Layout components
            window.setLocationRelativeTo(null);     // Center window.
            window.setVisible(true);
        }

        //========================================================== constructor
        public ClockAnalogBuf() {
            //... Create an instance of our new clock component.
            _clock = new Clock();

            //... Set the applet's layout and add the clock to it.
            setLayout(new BorderLayout());
            add(_clock, BorderLayout.CENTER);

            //... Start the clock running.
            start();
        }

        //=============================================================== start
        @Override public void start() {
            _clock.start();
        }

        //================================================================ stop
        @Override public void stop() {
            _clock.stop();
        }
    }

## A Clock component

      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
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    // File   : GUI-lowlevel/animation/analogclock/Clock.java
    // Purpose: An analog clock component -- Uses Timer and Calendar.
    // Note   : Uses a BufferedImage for clock face so isn't drawn each time.
    // Author : Fred Swartz, 1999-2007, Placed in public domain.

    import java.util.*;
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    import java.awt.image.*;

    //////////////////////////////////////////////////////////////// Clock class
    class Clock extends JComponent {

        //============================================================ constants
        private static final double TWO_PI   = 2.0 * Math.PI;

        private static final int    UPDATE_INTERVAL = 100;  // Millisecs

        //=============================================================== fields
        private Calendar _now = Calendar.getInstance();  // Current time.

        private int _diameter;                 // Height and width of clock face
        private int _centerX;                  // x coord of middle of clock
        private int _centerY;                  // y coord of middle of clock
        private BufferedImage _clockImage;     // Saved image of the clock face.

        private javax.swing.Timer _timer;      // Fires to update clock.

        //==================================================== Clock constructor
        public Clock() {
            setPreferredSize(new Dimension(300,300));

            _timer = new javax.swing.Timer(UPDATE_INTERVAL, new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    updateTime();
                    repaint();
                }
            });
        }

        //================================================================ start
        /** Start the timer. */
        public void start() {
            _timer.start();
        }

        //================================================================= stop
        /** Stop the timer. */
        public void stop() {
            _timer.stop();
        }

        //=========================================================== updateTime
        private void updateTime() {
            //... Avoid creating new objects.
            _now.setTimeInMillis(System.currentTimeMillis());
        }

        //======================================================= paintComponent
        @Override public void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D)g;
            g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                RenderingHints.VALUE_ANTIALIAS_ON);

            //... The panel may have been resized, get current dimensions
            int w = getWidth();
            int h = getHeight();
            _diameter = ((w < h) ? w : h);
            _centerX = _diameter / 2;
            _centerY = _diameter / 2;

            //... Create the clock face background image if this is the first time,
            //    or if the size of the panel has changed
            if (_clockImage == null
                    || _clockImage.getWidth() != w
                    || _clockImage.getHeight() != h) {
                _clockImage = (BufferedImage)(this.createImage(w, h));

                //... Get a graphics context from this image
                Graphics2D g2a = _clockImage.createGraphics();
                g2a.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                                     RenderingHints.VALUE_ANTIALIAS_ON);
                drawClockFace(g2a);
            }

            //... Draw the clock face from the precomputed image
            g2.drawImage(_clockImage, null, 0, 0);

            //... Draw the clock hands dynamically each time.
            drawClockHands(g2);
        }

        //====================================== convenience method drawClockHands
        private void drawClockHands(Graphics2D g2) {
            //... Get the various time elements from the Calendar object.
            int hours   = _now.get(Calendar.HOUR);
            int minutes = _now.get(Calendar.MINUTE);
            int seconds = _now.get(Calendar.SECOND);
            int millis  = _now.get(Calendar.MILLISECOND);

            //... second hand
            int handMin = _diameter / 8;    // Second hand doesn't start in middle.
            int handMax = _diameter / 2;    // Second hand extends to outer rim.
            double fseconds = (seconds + (double)millis/1000) / 60.0;
            drawRadius(g2, fseconds, 0, handMax);

            //... minute hand
            handMin = 0;                    // Minute hand starts in middle.
            handMax = _diameter / 3;
            double fminutes = (minutes + fseconds) / 60.0;
            drawRadius(g2, fminutes, 0, handMax);

            //... hour hand
            handMin = 0;
            handMax = _diameter / 4;
            drawRadius(g2, (hours + fminutes) / 12.0, 0, handMax);
        }

        //======================================= convenience method drawClockFace
        private void drawClockFace(Graphics2D g2) {
            // ... Draw the clock face.  Probably into a buffer.
            g2.setColor(Color.PINK);
            g2.fillOval(0, 0, _diameter, _diameter);
            g2.setColor(Color.BLACK);
            g2.drawOval(0, 0, _diameter, _diameter);

            int radius = _diameter / 2;

            //... Draw the tick marks around the circumference.
            for (int sec = 0; sec < 60; sec++) {
                int tickStart;
                if (sec%5 == 0) {
                    tickStart = radius - 10;  // Draw long tick mark every 5.
                } else {
                    tickStart = radius - 5;   // Short tick mark.
                }
                drawRadius(g2, sec / 60.0, tickStart , radius);
            }
        }

        //==================================== convenience method drawRadius
        // This draw lines along a radius from the clock face center.
        // By changing the parameters, it can be used to draw tick marks,
        // as well as the hands.
        private void drawRadius(Graphics2D g2, double percent,
                                int minRadius, int maxRadius) {
            //... percent parameter is the fraction (0.0 - 1.0) of the way
            //    clockwise from 12.   Because the Graphics2D methods use radians
            //    counterclockwise from 3, a little conversion is necessary.
            //    It took a little experimentation to get this right.
            double radians = (0.5 - percent) * TWO_PI;
            double sine   = Math.sin(radians);
            double cosine = Math.cos(radians);

            int dxmin = _centerX + (int)(minRadius * sine);
            int dymin = _centerY + (int)(minRadius * cosine);

            int dxmax = _centerX + (int)(maxRadius * sine);
            int dymax = _centerY + (int)(maxRadius * cosine);
            g2.drawLine(dxmin, dymin, dxmax, dymax);
        }
    }