Solution for
Programming Exercise 7.6


THIS PAGE DISCUSSES ONE POSSIBLE SOLUTION to the following exercise from this on-line Java textbook.

Exercise 7.6: The StopWatch component from Section 7.4 displays the text "Timing..." when the stop watch is running. It would be nice if it displayed the elapsed time since the stop watch was started. For that, you need to create a Timer. Add a Timer to the original source code, StopWatch.java, to display the elapsed time in seconds. Create the timer in the mousePressed() routine when the stop watch is started. Stop the timer in the mousePressed() routine when the stop watch is stopped. The elapsed time won't be very accurate anyway, so just show the integral number of seconds. You only need to set the text a few times per second. For my Timer method, I use a delay of 100 milliseconds for the timer. Here is an applet that tests my solution to this exercise:


Discussion

This one is almost too easy. (The hardest part is that I referred to the stop watch as a "timer" in my program, and this could be confused with a Timer object.)

The guidelines for using Timers can be found in Section 7.6. An instance variable named timer of type Timer is added to the class. The timer is started when the stop watch is started and is stopped when the stop watch is stopped. Both of these things happen in the mousePressed() method. I could have created a new timer each time the stop watch is started, but I decided to reuse a single timer. The first time the stop watch is started, a Timer object is created and started. After that, the same Timer object is simply restarted. I can tell the difference between these two cases since the first time, the timer will be null:

             if (timer == null) {
                timer = new Timer(100,this);
                timer.start();
             }
             else
                timer.restart();

The class is declared to implement ActionListener so that it can respond to events from the timer. (As always, it would probably be better style to create another object to do the listening.) The actionPerformed() method just has to set the text on the label to show how much time has passed since the stop watch was started. The starting time of the stop watch is in the instance variable startTime. The current time is given by System.currentTimeMillis(), so the elapsed time, in milliseconds, is just System.currentTimeMillis() - startTime. This has to be divided by 1000 to give the number of seconds. (Remember that dividing an integer by an integer always gives an integer. The answer is rounded down to the nearest integral number of seconds.) The actionPerformed() method simply does this calculation and then sets the text of the label.

The complete source code is shown below, followed by the source code for the little applet that tests the component. The applet sets the fonts and colors of the stop watch component.


The Solution

The improved StopWatch component, with changes shown in red:


    /*
       A component that acts as a simple stop-watch.  When the user clicks
       on it, this component starts timing.  When the user clicks again,
       it displays the time between the two clicks.  Clicking a third time
       starts another timer, etc.  While it is timing, the label just
       displays the whole number of seconds since the timer was started.
    */
    
    import java.awt.event.*;
    import javax.swing.*;

    public class StopWatchRunner extends JLabel 
                implements MouseListener, ActionListener {

       private long startTime;   // Start time of stopwatch.
                                 //   (Time is measured in milliseconds.)

       private boolean running;  // True when the stopwatch is running.

       private Timer timer;  // A timer that will generate events
                             // while the stopwatch is running

       public StopWatchRunner() {
             // Constructor.
          super("  Click to start timer.  ", JLabel.CENTER);
          addMouseListener(this);
       }

       public void actionPerformed(ActionEvent evt) {
              // This will be called when an event from the
              // timer is received.  It just sets the stopwatch
              // to show the amount of time that it has been running.
              // Time is rounded down to the nearest second.
           long time = (System.currentTimeMillis() - startTime) / 1000;
           setText("Running:  " + time + " seconds");
       }

       public void mousePressed(MouseEvent evt) {
              // React when user presses the mouse by
              // starting or stopping the stopwatch.  Also start
              // or stop the timer.
          if (running == false) {
                // Record the time and start the stopwatch.
             running = true;
             startTime = evt.getWhen();  // Time when mouse was clicked.
             setText("Running:  0 seconds");
             if (timer == null) {
                timer = new Timer(100,this);
                timer.start();
             }
             else
                timer.restart();
          }
          else {
                // Stop the stopwatch.  Compute the elapsed time since the
                // stopwatch was started and display it.
             timer.stop();
             running = false;
             long endTime = evt.getWhen();
             double seconds = (endTime - startTime) / 1000.0;
             setText("Time: " + seconds + " sec.");
          }
       }

       public void mouseReleased(MouseEvent evt) { }
       public void mouseClicked(MouseEvent evt) { }
       public void mouseEntered(MouseEvent evt) { }
       public void mouseExited(MouseEvent evt) { }

    }  // end StopWatchRunner

A small applet to test the component:

    /*
       A trivial applet that tests the StopWatchRunner component.
       The applet just creates and shows a StopWatchRunner.
    */
    
    import java.awt.*;
    import javax.swing.*;
    
    public class TestStopWatch extends JApplet {
    
       public void init() {
          
          StopWatchRunner watch = new StopWatchRunner();
          watch.setFont( new Font("SansSerif", Font.BOLD, 24) );
          watch.setBackground(Color.white);
          watch.setForeground( new Color(180,0,0) );
          watch.setOpaque(true);
          getContentPane().add(watch, BorderLayout.CENTER);
          
       }
    
    }


[ Exercises | Chapter Index | Main Index ]