Hi there,

I need to write a program that draws a clock face with a time that the user enters in two text fields (one for the hours, one for the minutes).

As far as I can tell I have all the code written, it's now just a matter of drawing the clock (even though it looks like it *should* draw).

Even though I have added the ClockViewerComponent to the JPanel in ClockViewerFrame, it doesn't draw before or after inputing the hour and minute. I can not figure out why :S. As I understand it, it should draw a clock with time 12:00 before any input - and then change accordingly after the input and click of the 'draw' button. Any tips/pointers in a mistake I made - or something I missed, would be very much appreciated.

import javax.swing.JFrame;

/**
 * This program displays a clock face with a time
 * the the user specified.
 */
public class ClockViewer {

	public static void main (String[] args) {		
		JFrame frame = new ClockViewerFrame();
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setTitle("ClockViewer");
                frame.setVisible(true);
	}
}
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.util.Random;
import java.awt.geom.Line2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;

/**
 * This programs draws a clock based on the hour and minutes.
 */
public class ClockViewerComponent extends JComponent {
   
   private double hours;
   private double minutes;
   private final double RADIUS = 100;
   private final double MINUTES_PER_HOUR = 60;
   private final double HOURS_PER_DAY = 12;
   private final double HOUR_HAND = 75;
   private final double MINUTE_HAND = 90;

   /**
    * Initilizes the component with a default time of 12:00.
    */
   public ClockViewerComponent() {

      hours = 12;
      minutes = 0;
   }
   
   /**
    * Sets the time of the clock.
    * @param anHour the hour
    * @param aMinute the minute(s)
    */
   public void setHourAndMinute(double anHour, double aMinute) {

      minutes = aMinute;
      hours = anHour;
   }
   
   /**
    * Paints the clock within the JFrame.
    */
   public void paintComponent(Graphics g) {
      
      Graphics2D g2 = (Graphics2D) g;
      
      Ellipse2D.Double face = new Ellipse2D.Double(0, 0, 2 * RADIUS,
         2 * RADIUS);
      g2.draw(face);

      Point2D.Double center = new Point2D.Double(RADIUS, RADIUS);

      double angle = Math.PI /2 - 2 * Math.PI * minutes / MINUTES_PER_HOUR;

      Point2D.Double minutePoint = new Point2D.Double(RADIUS + MINUTE_HAND
         * Math.cos(angle), RADIUS - MINUTE_HAND * Math.sin(angle));
      Line2D.Double minuteHand = new Line2D.Double(center, minutePoint);
      g2.draw(minuteHand);

      angle = Math.PI / 2 - 2 * Math.PI * (hours * MINUTES_PER_HOUR +
         minutes) / (MINUTES_PER_HOUR * HOURS_PER_DAY);

      Point2D.Double hourPoint = new Point2D.Double(RADIUS + HOUR_HAND *
         Math.cos(angle), RADIUS - HOUR_HAND * Math.sin(angle));
      Line2D.Double hourHand = new Line2D.Double(center, hourPoint);
      g2.draw(hourHand);

   }
}
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComponent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;

/**
 * This program creates a template JFrame for our clock.
 */
public class ClockViewerFrame extends JFrame {

   private JLabel hourLabel;
   private JLabel minuteLabel;
   private JTextField hourField;
   private JTextField minuteField;
   private JButton button;
   private JPanel panel;

   private final int FRAME_WIDTH = 400;
   private final int FRAME_HEIGHT = 400;
   
   final ClockViewerComponent component;
   
   /**
    * Initializes the framer with the ClockViewer component,
    * helper methods, and the frame size.
    */
   public ClockViewerFrame() {

      component = new ClockViewerComponent();

      createTextField();
      createButton();
      createPanel();

      setSize(FRAME_WIDTH, FRAME_HEIGHT);
   }

   /**
    * Creates the label and text field for the user to read,
    * and input desired values.
    */
   public void createTextField() {

      final int FIELD_WIDTH = 5;
      
      hourLabel = new JLabel("Hour = ");
      hourField = new JTextField(FIELD_WIDTH);
      hourField.setText("");
      
      minuteLabel = new JLabel("Minute = ");
      minuteField = new JTextField(FIELD_WIDTH);
      minuteField.setText("");
   }
   
   /**
    * Creates the button the user clicks when finishing entering input,
    * Takes the input and passes it to the component.
    */
   public void createButton() {

      button = new JButton("Draw");
      
      class DrawListener implements ActionListener {

         public void actionPerformed(ActionEvent event) {

            double hour = Double.parseDouble(hourField.getText());
            double minute = Double.parseDouble(minuteField.getText());

            component.setHourAndMinute(hour, minute);
            component.repaint();
         }
      }

      ActionListener listener = new DrawListener();
      button.addActionListener(listener);       
   }
   
   /**
    * Adds the panel and adds each part to it.
    */
   private void createPanel() {

      panel = new JPanel();
      panel.add(hourLabel);
      panel.add(hourField);
      panel.add(minuteLabel);
      panel.add(minuteField);
      panel.add(button);
      panel.add(component);
      add(panel);
   }
}
Comments
Code tags on first post!

If you specify a LayoutManager for panel, your clock drawing will become visible. Below, GridLayout is specified. It's not a good choice for you (BorderLayout would be better, I think), but your clock is at least visible. You'll need to add an import for GridLayout at the top.

/**
    * Adds the panel and adds each part to it.
    */
   private void createPanel() {

      panel = new JPanel();
      JPanel inputPanel = new JPanel ();
      panel.setLayout (new GridLayout (2, 1));
      inputPanel.add(hourLabel);
      inputPanel.add(hourField);
      inputPanel.add(minuteLabel);
      inputPanel.add(minuteField);
      inputPanel.add(button);
      panel.add (inputPanel);
      panel.add(component);
      this.getContentPane ().add(panel);
   }

Your clock viewer component currently has no size because it's an empty container and you have used the default flow layout. It's drawing on the panel, you just can't see it because it has no dimensions. Here's an alternate layout and setup for that panel that does show the clock.

private void createPanel() {
      panel = new JPanel(new BorderLayout());
      
      JPanel controlPanel = new JPanel();
      controlPanel.add(hourLabel);
      controlPanel.add(hourField);
      controlPanel.add(minuteLabel);
      controlPanel.add(minuteField);
      controlPanel.add(button);
      
      panel.add(controlPanel, BorderLayout.NORTH);
      panel.add(component,BorderLayout.CENTER);
      add(panel);
   }

You can learn more about using the various layout managers that are available here: http://java.sun.com/docs/books/tutorial/uiswing/layout/index.html

Edit: Doh! Vernon beat me to the post.

Wow - thank you both for the very prompt and professional responses.

What you both have posted makes total sense, the only problem I looked through the textbook chapter - and everything before - and I don't see any references to java.awt.BorderLayout or java.awt.GridLayout.

I suppose it is possible they expect us to find our own methods, but usually this wasn't the case. Is it possible that there is another way to accomplish what I am trying to do in another, perhaps less efficient - but basic way?

Thanks again!

Ah! I figured out the other solution. I guess by default components have zero width and height right.

Needed to add this:
component.setPreferredSize(new Dimension(325, 325));

Wow - thank you both for the very prompt and professional responses.

What you both have posted makes total sense, the only problem I looked through the textbook chapter - and everything before - and I don't see any references to java.awt.BorderLayout or java.awt.GridLayout.

I suppose it is possible they expect us to find our own methods, but usually this wasn't the case. Is it possible that there is another way to accomplish what I am trying to do in another, perhaps less efficient - but basic way?

Thanks again!

You're welcome. Here are links to the layout managers.

http://java.sun.com/javase/6/docs/api/java/awt/GridLayout.html
http://java.sun.com/javase/6/docs/api/java/awt/BorderLayout.html

If you don't want to use a layout manager, you can set a preferred size for component. Add this line before you add component to panel.

component.setPreferredSize(new Dimension (250, 250));

Have they covered preferred sizes?

[edit]
Guess you figured it out on your own!
[/edit]

This question has already been answered. Start a new discussion instead.