Animation by Example: Adding Controls
by David Matuszek

Now it's time to add some controls to our applet. We will do the simple thing and add a single button, labeled Start. When clicked, the animation will begin, and the label on the button will change to Stop.

Here's the applet:

test.html
Controller.java

In the Controller class, we create a new Panel and a Button to put on it:

	// Declare components here, where they are visible to inner classes
	Panel buttonPanel = new Panel ();
	Button runButton = new Button ("  Start  ");
In the Controller class's init() method, we add these components to the applet:
    buttonPanel.add (runButton);
    buttonPanel.setBackground(Color.lightGray);
    this.add (BorderLayout.SOUTH, buttonPanel);
Also in the init() method, we attach an action to the button. Fortunately, the View already checks an okToRun variable (see, we were thinking ahead!), so all we have to do is set that variable appropriately and change the label on the button.
	// Attach actions to components
    runButton.addActionListener (new ActionListener () {
      public void actionPerformed (ActionEvent event) {
        if (view.okToRun) {
          view.okToRun = false;
          showStatus ("Animation is stopped.");
          runButton.setLabel ("  Start  ");
        }
        else {
          view.okToRun = true;
          showStatus ("Animation is running.");
          runButton.setLabel ("  Stop   ");
       }
      }});

Multithreaded programs can have race conditions--if two Threads use the same variable, who gets it first?

It's worth looking at the View.run() method:

   public void run() {
     // "alive" will be true until the Applet is destroyed
     while(alive) {
       // "okToRun" will be true if the Applet is started and not stopped
       if(okToRun) {
         model.makeOneStep();
         repaint(); // Note: uses Graphics for this Canvas, not for the Applet
       }
       // Control the speed of execution
       try { Thread.sleep(delay); }
       catch(InterruptedException e) {}
     }
   }

This method uses two variables, alive and okToRun, to control execution.

When the run() method finishes, its Thread dies. When we quit the applet, we want this to happen. We want the Thread to die, otherwise it keeps running and slows down our computer. We can do this by setting alive to false. But we have to be sure to set alive to true before starting this Thread, otherwise the Thread might start, check alive, and die, before the applet even gets started.

The okToRun variable doesn't kill the thread, it just keeps it from updating the model or calling repaint() (which calls update(Graphics g)).But run() still has to sleep for a while (so it doesn't waste computer time that the other Threads could be using). However, run() isn't the only way to get to update(Graphics g) or paint(Graphics g); the Applet calls these directly when it first starts, and when the applet is covered and uncovered. So these two methods also have to check okToRun. Since this applet isn't supposed to do anything until you click the Start button, okToRun must be set to false before starting this Thread.

The final sequence (in the controller) looks like this:

    // Get everything set up for the view BEFORE starting its Thread
    view.alive = true;
    view.okToRun = false;
    
    // Give the view its own thread in which to do the animation
    animation = new Thread(view);
    animation.start();

Here are some race bugs that I've had and (hopefully) fixed with this program:

If you see one of these problems, maybe you have a race condition.

There is no simple solution for race conditions. They can all be solved by careful thought, but it isn't easy, and there are a lot of books on the subject. The important thing to keep in mind is that when you start a new Thread, you cannot know whether the current Thread will continue for a while, or whether the new Thread will take over immediately. Your own computer won't always do the same thing. One thing that helps is to get everything initialized and ready for the new Thread before you start it.

Previous