How do I write a simple analog clock using Java 2D?

Here is a simple Java code for an analog clock using Java 2D features in Swing.

Please adjust the code according to your needs. This code will create a new JFrame and continually update it every second with the current time.

package org.kodejava.swing;

import javax.swing.*;
import java.awt.*;
import java.util.Calendar;
import java.util.GregorianCalendar;

public class AnalogClock extends JPanel {

    public AnalogClock() {
        setPreferredSize(new Dimension(400, 300));
        setBackground(Color.WHITE);
        new Timer(1000, e -> repaint()).start();
    }

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

        int side = Math.min(getWidth(), getHeight());
        int centerX = getWidth() / 2;
        int centerY = getHeight() / 2;

        GregorianCalendar time = new GregorianCalendar();
        int second = time.get(Calendar.SECOND);
        int minute = time.get(Calendar.MINUTE);
        int hour = time.get(Calendar.HOUR_OF_DAY);

        drawHand(g2d, side/2 - 10, second / 60.0, 0.5f, Color.RED);
        drawHand(g2d, side/2 - 20, minute / 60.0, 2.0f, Color.BLUE);
        drawHand(g2d, side/2 - 40, hour / 12.0, 4.0f, Color.BLACK);

        // Draw clock numbers and circle
        drawClockFace(g2d, centerX, centerY, side/2 - 40);
    }

    private void drawHand(Graphics2D g2d, int length, double value, float stroke, Color color) {
        double angle = Math.PI * 2 * (value - 0.25);
        int endX = (int) (getWidth() / 2 + length * Math.cos(angle));
        int endY = (int) (getHeight() / 2 + length * Math.sin(angle));

        g2d.setColor(color);
        g2d.setStroke(new BasicStroke(stroke));
        g2d.drawLine(getWidth() / 2, getHeight() / 2, endX, endY);
    }

    // Added method to draw the clock face and numbers
    private void drawClockFace(Graphics2D g2d, int centerX, int centerY, int radius) {
        g2d.setStroke(new BasicStroke(2.0f));
        g2d.setColor(Color.BLACK);
        g2d.drawOval(centerX - radius, centerY - radius, 2 * radius, 2 * radius);

        for (int i = 1; i <= 12; i++) {
            double angle = Math.PI * 2 * (i / 12.0 - 0.25);
            int dx = centerX + (int) ((radius + 20) * Math.cos(angle));
            int dy = centerY + (int) ((radius + 20) * Math.sin(angle));

            g2d.drawString(Integer.toString(i), dx, dy);
        }
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("Analog Clock");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new AnalogClock());
        frame.pack();
        frame.setVisible(true);
    }
}

In this code, each “hand” of the clock is moved by calculating its angle according to the current time. The drawHand() method takes the length of the hand, the proportion of its rotation (for an hour hand, this would be the current hour divided by 12), the stroke width to draw with, and the color to draw, then calculates the end point of the hand line and draws it from the center of the clock.

The drawClockFace(), which draws a circle using the drawOval() method (because a circle is a type of oval), and adds numbers around the edge of the circle using the drawString() method. In both cases, the position of each element is calculated based on the sine and cosine of the angle that represents each hour on the clock face.

Analog Clock

Analog Clock

Note: Swing is an old but reliable technology that allows coding of GUI elements, however, it’s no longer actively developed. If you are developing a new project, consider using JavaFX as it is more modern and actively developed.

How do I change JFrame state programmatically?

In the following code snippet you’ll learn how to programmatically change the frame state of a JFrame component in a Swing application. The JFrame states are represented as a bitwise masks. You can minimize, maximize or make the JFrame state to normal, using the JFrame.setExtendedState() method.

You can pass the following values as the parameter to the method:

  • Frame.NORMAL
  • Frame.ICONIFIED
  • Frame.MAXIMIZED_HORIZ
  • Frame.MAXIMIZED_VERT
  • Frame.MAXIMIZED_BOTH
package org.kodejava.swing;

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

public class SwingFrameState extends JFrame {
    public SwingFrameState() throws HeadlessException {
        initUI();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(
                () -> new SwingFrameState().setVisible(true));
    }

    private void initUI() {
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        setLayout(new FlowLayout());

        final JButton minimize = new JButton("Minimize");
        final JButton maximize = new JButton("Maximize");
        final JButton normal = new JButton("Normal");

        add(normal);
        add(minimize);
        add(maximize);

        pack();
        setSize(500, 200);

        ActionListener listener = e -> {
            if (e.getSource() == normal) {
                setExtendedState(Frame.NORMAL);
            } else if (e.getSource() == minimize) {
                setExtendedState(Frame.ICONIFIED);
            } else if (e.getSource() == maximize) {
                setExtendedState(Frame.MAXIMIZED_BOTH);
            }
        };

        minimize.addActionListener(listener);
        maximize.addActionListener(listener);
        normal.addActionListener(listener);
    }
}

The screenshot of the output from the code snippet above is:

Change JFrame State Programmatically

Change JFrame State Programmatically

How do I get the JFrame of a component?

This code snippet gives an example on how to get the JFrame of a component. In this example we try to get the JFrame from a button action listener event. To get the JFrame we utilize the SwingUtilities.getRoot() method and this will return the root component of the component tree in out small program below which is a JFrame.

Beside getting the JFrame, in this program we also do small stuff like changing the frame background color by a random color every time a user presses the “Change Frame Color” button.

package org.kodejava.swing;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.HeadlessException;

public class GetFrameDemo extends JFrame {
    public GetFrameDemo() throws HeadlessException {
        initialize();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new GetFrameDemo().setVisible(true));
    }

    private void initialize() {
        this.setSize(500, 500);
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        this.setLayout(new FlowLayout(FlowLayout.CENTER));

        final JLabel rLabel = new JLabel("r: ");
        final JLabel gLabel = new JLabel("g: ");
        final JLabel bLabel = new JLabel("b: ");

        JButton button = new JButton("Change Frame Background Color");
        button.addActionListener(e -> {
            Component component = (Component) e.getSource();

            // Returns the root component for the current component tree
            JFrame frame = (JFrame) SwingUtilities.getRoot(component);

            int r = (int) (Math.random() * 255);
            int g = (int) (Math.random() * 255);
            int b = (int) (Math.random() * 255);

            rLabel.setText("r: " + r);
            gLabel.setText("g: " + g);
            bLabel.setText("b: " + b);

            frame.getContentPane().setBackground(new Color(r, g, b));
        });

        this.getContentPane().add(button);
        this.getContentPane().add(rLabel);
        this.getContentPane().add(gLabel);
        this.getContentPane().add(bLabel);
    }
}
Get Swing Root Component

Get Swing Root Component

How do I disable JFrame close button?

To disable the close operation on a JFrame, when user click the close icon on the JFrame, we can set the value of the default close operation to WindowConstants.DO_NOTHING_ON_CLOSE.

package org.kodejava.swing;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.HeadlessException;

public class DisableCloseButtonDemo extends JFrame {
    public DisableCloseButtonDemo() throws HeadlessException {
        initialize();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new DisableCloseButtonDemo().setVisible(true));
    }

    private void initialize() {
        // WindowConstants.DO_NOTHING_ON_CLOSE tell JFrame instance to do
        // nothing when a window closing event occurs.
        this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);

        JButton button = new JButton("Close");
        button.addActionListener(e -> System.exit(0));

        this.setLayout(new FlowLayout(FlowLayout.CENTER));
        this.setSize(new Dimension(500, 500));
        this.getContentPane().add(button);
    }
}

How do I handle JFrame window events?

This example show you how to handle JFrame window events such as windowOpened, windowClosing, windowClosed, etc. For handling these events we need to add a WindowListener listener to the JFrame instance. Here we use the WindowAdapter abstract class and implement the method which event we want to handle.

package org.kodejava.swing;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class WindowListenerDemo extends JFrame {
    public WindowListenerDemo() {
        initializeComponent();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new WindowListenerDemo().setVisible(true));
    }

    private void initializeComponent() {
        setSize(500, 500);
        setTitle("Window Listener");
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        this.addWindowListener(new WindowAdapter() {
            // Invoked when a window has been opened.
            public void windowOpened(WindowEvent e) {
                System.out.println("Window Opened Event");
            }

            // Invoked when a window is in the process of being closed.
            // The close operation can be overridden at this point.
            public void windowClosing(WindowEvent e) {
                System.out.println("Window Closing Event");
            }

            // Invoked when a window has been closed.
            public void windowClosed(WindowEvent e) {
                System.out.println("Window Close Event");
            }

            // Invoked when a window is iconified.
            public void windowIconified(WindowEvent e) {
                System.out.println("Window Iconified Event");
            }

            // Invoked when a window is de-iconified.
            public void windowDeiconified(WindowEvent e) {
                System.out.println("Window Deiconified Event");
            }

            // Invoked when a window is activated.
            public void windowActivated(WindowEvent e) {
                System.out.println("Window Activated Event");
            }

            // Invoked when a window is de-activated.
            public void windowDeactivated(WindowEvent e) {
                System.out.println("Window Deactivated Event");
            }

            // Invoked when a window state is changed.
            public void windowStateChanged(WindowEvent e) {
                System.out.println("Window State Changed Event");
            }

            // Invoked when the Window is set to be the focused Window, which means
            // that the Window, or one of its sub components, will receive keyboard
            // events.
            public void windowGainedFocus(WindowEvent e) {
                System.out.println("Window Gained Focus Event");
            }

            // Invoked when the Window is no longer the focused Window, which means
            // that keyboard events will no longer be delivered to the Window or any of
            // its sub components.
            public void windowLostFocus(WindowEvent e) {
                System.out.println("Window Lost Focus Event");
            }
        });
    }
}