Java: Example - Buffered Analog Clock
The following is an analog (uses hands) clock that uses
buffered drawing to increase speed. In buffered drawing some or
all of an images 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.
// An analog clock -- Using Timer and Calendar.
// Uses a BufferedImage and antialiasing.
// The second hand creep is from the Timer, which seems to take
// a little longer than the requested time to call the listener.
// -- Fred Swartz, 1 May 1999, 2002-02-21 from applet to application
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;
///////////////////////////////////////////////////////////// ClockAnalogBuf
public class ClockAnalogBuf extends JFrame {
Clock clockFace;
//================================================================= main
public static void main(String[] args) {
JFrame windo = new ClockAnalogBuf();
windo.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
windo.setVisible(true);
}//end main
//========================================================== constructor
public ClockAnalogBuf() {
Container content = this.getContentPane();
content.setLayout(new BorderLayout());
clockFace = new Clock();
content.add(clockFace, BorderLayout.CENTER);
this.setTitle("Analog Clock");
this.pack();
clockFace.start();
}//end constructor
}//end class ClockAnalogBuf
//////////////////////////////////////////////////////////////// Clock class
class Clock extends JPanel {
private int hours = 0;
private int minutes = 0;
private int seconds = 0;
private int millis = 0;
private static final int spacing = 10;
private static final float twoPi = (float)(2.0 * Math.PI);
private static final float threePi = (float)(3.0 * Math.PI);
// Angles for the trigonometric functions are measured in radians.
// The following in the number of radians per sec or min.
private static final float radPerSecMin = (float)(Math.PI / 30.0);
private int size; // 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;
private javax.swing.Timer t;
//==================================================== Clock constructor
public Clock() {
this.setPreferredSize(new Dimension(300,300));
this.setBackground(Color.white);
this.setForeground(Color.black);
t = new javax.swing.Timer(1000,
new ActionListener() {
public void actionPerformed(ActionEvent e) {
update();
}
});
}//end constructor
//=============================================================== update
// Replace the default update so that the plain background
// doesn't get drawn.
public void update() {
// Hmmm, is this the right way to do this? Or do we just
// call paintComponent? Does this coalesce calls, not that
// this updates often enuf for that to be important.
this.repaint();
}//end update
//================================================================ start
public void start() {
t.start(); // start the timer
}//end start
//================================================================= stop
public void stop() {
t.stop(); // start the timer
}//end stop
//======================================================= paintComponent
public void paintComponent(Graphics g) {
super.paintComponent(g); // paint background, borders
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();
size = ((w<h) ? w : h) - 2*spacing;
centerX = size/2 + spacing;
centerY = size/2 + spacing;
// 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));
// now get a graphics context from this image
Graphics2D gc = clockImage.createGraphics();
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawClockFace(gc);
}
// Now get the time and draw the hands.
Calendar now = Calendar.getInstance();
hours = now.get(Calendar.HOUR);
minutes = now.get(Calendar.MINUTE);
seconds = now.get(Calendar.SECOND);
millis = now.get(Calendar.MILLISECOND);
// Draw the clock face from the precomputed image
g2.drawImage(clockImage, null, 0, 0);
// Draw the clock hands
drawClockHands(g);
}//end paintComponent
//======================================================= drawClockHands
private void drawClockHands(Graphics g) {
int secondRadius = size/2;
int minuteRadius = secondRadius * 3/4;
int hourRadius = secondRadius/2;
// second hand
float fseconds = seconds + (float)millis/1000;
float secondAngle = threePi - (radPerSecMin * fseconds);
drawRadius(g, centerX, centerY, secondAngle, 0, secondRadius);
// minute hand
float fminutes = (float)(minutes + fseconds/60.0);
float minuteAngle = threePi - (radPerSecMin * fminutes);
drawRadius(g, centerX, centerY, minuteAngle, 0, minuteRadius);
// hour hand
float fhours = (float)(hours + fminutes/60.0);
float hourAngle = threePi - (5 * radPerSecMin * fhours);
drawRadius(g, centerX, centerY, hourAngle, 0, hourRadius);
}//end drawClockHands
//======================================================== drawClockFace
private void drawClockFace(Graphics g) {
// clock face
g.setColor(Color.pink);
g.fillOval(spacing, spacing, size, size);
g.setColor(Color.black);
g.drawOval(spacing, spacing, size, size);
// tic marks
for (int sec = 0; sec<60; sec++) {
int ticStart;
if (sec%5 == 0) {
ticStart = size/2-10;
} else {
ticStart = size/2-5;
}
drawRadius(g, centerX, centerY, radPerSecMin*sec, ticStart , size/2);
}
}//endmethod drawClockFace
//=========================================================== drawRadius
private void drawRadius(Graphics g, int x, int y, double angle,
int minRadius, int maxRadius) {
float sine = (float)Math.sin(angle);
float cosine = (float)Math.cos(angle);
int dxmin = (int)(minRadius * sine);
int dymin = (int)(minRadius * cosine);
int dxmax = (int)(maxRadius * sine);
int dymax = (int)(maxRadius * cosine);
g.drawLine(x+dxmin, y+dymin, x+dxmax, y+dymax);
}//end drawRadius
}//endclass Clock