Example: Bouncing Balls

In the bouncing ball applet each ball is animated by its own slave thread. New balls are created by mouse clicks. Keyboard inputs stop, suspend, and resume the balls:

If you have a JDK 1.1 enabled browser, the applet is located at:

 http://www.cs.sjsu.edu/faculty/pearce/java1/threads/balls/bounce.html

Imports

import java.awt.*;
import java.awt.event.*;
import java.util.*;     // for Vector
import java.applet.*;

Class Declaration and Member Variables

public class Bounce extends Applet {
   // client area metrics
   protected int xUpperLeft, yUpperLeft, xLowerRight, yLowerRight;
   private Vector balls;
   private int ballDiam;
   private Random numberGen;
   private boolean paused;

Stopping, Suspending, and Resuming the Balls

   private void killAll() {
      showStatus("Killing all balls");
      for(int i = 0; i < balls.size(); i++) {
         Ball b = (Ball)balls.elementAt(i);
         b.stop();
      }
      balls.removeAllElements();
      repaint();
   }

   private void resumeAll() {
      if (paused) {
         showStatus("Resuming all balls");
         paused = false;
         for(int i = 0; i < balls.size(); i++) {
            Ball b = (Ball)balls.elementAt(i);
            if (!b.isAlive())
               showStatus("This ball is dead!");
            b.resume();
         }
      }
   }

   private void suspendAll() {
      if (!paused) {
         showStatus("Suspending all balls");
         paused = true;
         for(int i = 0; i < balls.size(); i++) {
            Ball b = (Ball)balls.elementAt(i);
               b.suspend();
         }
      }
   }

start(), stop(), and destroy()

   public void start() { updateMetrics(); resumeAll(); }
   public void stop() { suspendAll(); }
   public void destroy() { killAll(); }

Initializing the Applet

   public void init() {
      addMouseListener(new MouseEventHandler());
      addKeyListener(new KeyEventHandler());
      Object parent = getParent();
      balls = new Vector();
      ballDiam = 10;
      numberGen = new Random();
      paused = false;
   }

Initializing Client Area Metrics

   protected void updateMetrics() {
      Dimension d = getSize();
      Insets in = getInsets();
      int clientWidth = d.width - in.right - in.left;
      int clientHeight = d.height - in.bottom - in.top;
      xUpperLeft = in.right;
      yUpperLeft = in.top;
      xLowerRight = xUpperLeft + clientWidth;
      yLowerRight = yUpperLeft + clientHeight;
   }

Keyboard Listener

   class KeyEventHandler extends KeyAdapter {
      public void keyTyped(KeyEvent e) {
         // int key = e.getKeyCode(); // always 0!
         char key = e.getKeyChar();
         if (key == 'k') killAll();
         else if (key == 'r') resumeAll();
         else if (key == 's') suspendAll();
         else showStatus("key pressed = " + key);
      }
   }

Mouse Listener

   class MouseEventHandler extends MouseAdapter {
      public void mouseClicked(MouseEvent e) {
         showStatus("New Ball");
         Point p = e.getPoint();
         Ball b = new Ball(p.x, p.y);
         balls.addElement(b);
         b.start();
      }

      public void mouseReleased(MouseEvent e) {
         showStatus("");
      }
   }

paint()

   public void paint(Graphics g) {
      for(int i = 0; i < balls.size(); i++) {
         Ball b = (Ball)balls.elementAt(i);
         g.setColor(b.color);
         g.fillOval(b.xc, b.yc, ballDiam, ballDiam);
      }
   }

Balls

   // slave thread
   class Ball extends Thread {
      public int xc, yc;
      private int dx, dy, oldxc, oldyc, red, green, blue;
      public Color color;

      public Ball(int x, int y) {
         xc = x;
         yc = y;
         dx = numGen.nextInt() % 7 - 3;
         dy = numGen.nextInt() % 7 - 3;
         int max = Thread.MAX_PRIORITY + Thread.MIN_PRIORITY + 1;
         int pri = (numGen.nextInt() % max) - Thread.MIN_PRIORITY;
         red = numberGen.nextInt() % 256;
         blue = numberGen.nextInt() % 256;
         green = numberGen.nextInt() % 256;
         color = new Color(red, green, blue);
      }

      private void move() {
         oldxc = xc;
         oldyc = yc;
         xc += dx;
         yc += dy;
         if (xc <= xUpperLeft || xLowerRight <= xc)
            dx = -dx;
         if (yc <= yUpperLeft || yLowerRight <= yc)
            dy = -dy;
      }

      public void run() {
         while(true) {
            move();
            repaint(oldxc, oldyc, 2 * ballDiam, 2 * ballDiam);
            try { Thread.sleep(5); }
            catch(InterruptedException e) {}
         }
      }
   }
} // Bounce

HTML

<HTML>
<TITLE> Bouncing Balls </TITLE>
<BODY>
Mouse click creates a ball. Press s to suspend, r to resume, and k to kill.<BR>
<APPLET CODE="Bounce.class" WIDTH=300 HEIGHT=500>
   Sorry, your browser can't show applets.
</APPLET>
</BODY>
</HTML>