CS396N - Web Programming
Spring 2002
Chapter 16 - Concurrent Programming with Threads

Loading images - Redux

Example

import java.awt.*;
import java.applet.*;
import java.net.*;
public class Image1 extends Applet{ URL codebase;
Image picture;
public void init() {
     codebase = getDocumentBase(); picture = getImage(codebase,"ny1.jpg"); }
public void paint(Graphics g) { g.drawImage(picture,10,10,this); }
}
Example


The following program loads an image from disk within a rectangular area:

import java.awt.*;
import java.applet.*;
import java.net.*;

public class Image2 extends Applet{

URL codebase;
Image picture;
public void init(){ codebase = getDocumentBase();

picture = getImage(codebase,"ny1.jpg");

}
public void paint(Graphics g){ g.drawImage(picture,10,10,300,300,this); } }


Note


Using off-screen buffering

Sample Sequence of  Programs

Example 1 - Circle Draw - Uses Paint to Draw


import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;

// An applet that draws a small circle where you click.
 

public class DrawCircles extends Applet {

  private Vector circles;

  /** When you click the mouse, create a SimpleCircle,
   *  put it in the Vector, and tell the system
   *  to repaint (which calls update, which clears
   *  the screen and calls paint).
   */

  private class CircleDrawer extends MouseAdapter {
    public void mousePressed(MouseEvent event) {
      circles.addElement(
         new SimpleCircle(event.getX(),event.getY(),25));
      repaint();
    }
  }

  public void init() {
    circles = new Vector();
    addMouseListener(new CircleDrawer());
    setBackground(Color.white);
  }

  /** This loops down the available SimpleCircle objects,
   *  drawing each one.
   */

  public void paint(Graphics g) {
    SimpleCircle circle;
    for(int i=0; i<circles.size(); i++) {
      circle = (SimpleCircle)circles.elementAt(i);
      circle.draw(g);
    }
  }
}

import java.awt.*;

/** A class to store an x, y, and radius, plus a draw method.
 */

public class SimpleCircle {
  private int x, y, radius;

  public SimpleCircle(int x, int y, int radius) {
    setX(x);
    setY(y);
    setRadius(radius);
  }

  /** Given a Graphics, draw the SimpleCircle
   *  centered around its current position.
   */

  public void draw(Graphics g) {
    g.fillOval(x - radius, y - radius,
               radius * 2, radius * 2);
  }

  public int getX() { return(x); }

  public void setX(int x) { this.x = x; }

  public int getY() { return(y); }

  public void setY(int y) { this.y = y; }

  public int getRadius() { return(radius); }

  public void setRadius(int radius) {
    this.radius = radius;
  }
}

Example 2 - Bounce - Uses Paint to Draw - Shows Flicker


import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;

/** Bounce circles around on the screen. Doesn't use double
 *  buffering, so has problems with overlapping circles.
 *  Overrides update to avoid flicker problems.
 */

public class Bounce extends Applet implements Runnable,
                                              ActionListener {
  private Vector circles;
  private int width, height;
  private Button startButton, stopButton;
  private Thread animationThread = null;

  public void init() {
    setBackground(Color.white);
    width = getSize().width;
    height = getSize().height;
    circles = new Vector();
    startButton = new Button("Start a circle");
    startButton.addActionListener(this);
    add(startButton);
    stopButton = new Button("Stop all circles");
    stopButton.addActionListener(this);
    add(stopButton);
  }

  /** When the "start" button is pressed, start the animation
   *  thread if it is not already started. Either way, add a
   *  circle to the Vector of circles that are being bounced.
   *  <P>
   *  When the "stop" button is pressed, stop the thread and
   *  clear the Vector of circles.
   */

  public void actionPerformed(ActionEvent event) {
    if (event.getSource() == startButton) {
      if (circles.size() == 0) {
        // Erase any circles from previous run.
        getGraphics().clearRect(0, 0, getSize().width,
                                      getSize().height);
        animationThread = new Thread(this);
        animationThread.start();
      }
      int radius = 25;
      int x = radius + randomInt(width - 2 * radius);
      int y = radius + randomInt(height - 2 * radius);
      int deltaX = 1 + randomInt(10);
      int deltaY = 1 + randomInt(10);
      circles.addElement(new MovingCircle(x, y, radius, deltaX,
                                          deltaY));
    } else if (event.getSource() == stopButton) {
      if (animationThread != null) {
        animationThread = null;
        circles.removeAllElements();
      }
    }
    repaint();
  }

  /** Each time around the loop, call paint and then take a
   *  short pause. The paint method will move the circles and
   *  draw them.
   */

  public void run() {
    Thread myThread = Thread.currentThread();
    // Really while animationThread not null
    while(animationThread==myThread) {
      repaint();
      pause(100);
    }
  }

  /** Skip the usual screen-clearing step of update so that
   *  there is no flicker between each drawing step.
   */

  public void update(Graphics g) {
    paint(g);
  }

  /** Erase each circle's old position, move it, then draw it
   *  in new location.
   */

  public void paint(Graphics g) {
    MovingCircle circle;
    for(int i=0; i<circles.size(); i++) {
      circle = (MovingCircle)circles.elementAt(i);
      g.setColor(getBackground());
      circle.draw(g);  // Old position.
      circle.move(width, height);
      g.setColor(getForeground());
      circle.draw(g);  // New position.
    }
  }

  // Returns an int from 0 to max (inclusive),
  // yielding max + 1 possible values.

  private int randomInt(int max) {
    double x =
      Math.floor((double)(max + 1) * Math.random());
    return((int)(Math.round(x)));
  }
 

  // Sleep for the specified amount of time.

  private void pause(int milliseconds) {
    try {
      Thread.sleep((long)milliseconds);
    } catch(InterruptedException ie) {}
  }
}

Example 3 - Double Buffered Bounce


import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.Vector;

/** Bounce circles around on the screen, using double buffering
 *  for speed and to avoid problems with overlapping circles.
 *  Overrides update to avoid flicker problems.
 */

public class DoubleBufferBounce extends Applet implements Runnable, ActionListener {
  private Vector circles;
  private int width, height;
  private Image offScreenImage;
  private Graphics offScreenGraphics;
  private Button startButton, stopButton;
  private Thread animationThread = null;

  public void init() {
    setBackground(Color.white);
    width = getSize().width;
    height = getSize().height;
    offScreenImage = createImage(width, height);
    offScreenGraphics = offScreenImage.getGraphics();
    // Automatic in some systems, not in others.
    offScreenGraphics.setColor(Color.black);
    circles = new Vector();
    startButton = new Button("Start a circle");
    startButton.addActionListener(this);
    add(startButton);
    stopButton = new Button("Stop all circles");
    stopButton.addActionListener(this);
    add(stopButton);
  }

  /** When the "start" button is pressed, start the animation
   *  thread if it is not already started. Either way, add a
   *  circle to the Vector of circles that are being bounced.
   *  <P>
   *  When the "stop" button is pressed, stop the thread and
   *  clear the Vector of circles.
   */

  public void actionPerformed(ActionEvent event) {
    if (event.getSource() == startButton) {
      if (circles.size() == 0) {
        animationThread = new Thread(this);
        animationThread.start();
      }
      int radius = 25;
      int x = radius + randomInt(width - 2 * radius);
      int y = radius + randomInt(height - 2 * radius);
      int deltaX = 1 + randomInt(10);
      int deltaY = 1 + randomInt(10);
      circles.addElement(new MovingCircle(x, y, radius, deltaX,
                                          deltaY));
      repaint();
    } else if (event.getSource() == stopButton) {
      if (animationThread != null) {
        animationThread = null;
        circles.removeAllElements();
      }
    }
  }

  /** Each time around the loop, move each circle based on its
   *  current position and deltaX/deltaY values. These values
   *  reverse when the circles reach the edge of the window.
   */

  public void run() {
    MovingCircle circle;
    Thread myThread = Thread.currentThread();
    // Really while animationThread not null.
    while(animationThread==myThread) {
      for(int j=0; j<circles.size(); j++) {
        circle = (MovingCircle)circles.elementAt(j);
        circle.move(width, height);
      }
      repaint();
      pause(100);
    }
  }

  /** Skip the usual screen-clearing step of update so that
   *  there is no flicker between each drawing step.
   */

  public void update(Graphics g) {
    paint(g);
  }
 

  /** Clear the off-screen pixmap, draw each circle onto it, then
   *  draw that pixmap onto the applet window.
   */

  public void paint(Graphics g) {
    offScreenGraphics.clearRect(0, 0, width, height);
    MovingCircle circle;
    for(int i=0; i<circles.size(); i++) {
      circle = (MovingCircle)circles.elementAt(i);
      circle.draw(offScreenGraphics);
    }
    g.drawImage(offScreenImage, 0, 0, this);
  }

  // Returns an int from 0 to max (inclusive), yielding max + 1
  // possible values.

  private int randomInt(int max) {
    double x = Math.floor((double)(max + 1) * Math.random());
    return((int)(Math.round(x)));
  }

  // Sleep for the specified amount of time.

  private void pause(int milliseconds) {
    try {
      Thread.sleep((long)milliseconds);
    } catch(InterruptedException ie) {}
  }
}
 

Animation



Threads and animation

What are threads?

Use of threads in animation Example - Two Clocks

  //Sample Digital Clock Program showing two threads

import java.awt.*;
import java.util.Calendar;
import java.applet.*;

public class DigitalClock extends Applet implements Runnable{
 
 Thread clockThread1 = null, clockThread2 = null;
 Font font = new Font("Courier", Font.BOLD, 48);
 Color color = Color.green;
 
 public void start(){
  if (clockThread1 == null){
   clockThread1 = new Thread(this);
   clockThread1.start();
  }
  if (clockThread2 == null){
   clockThread2 = new Thread(this);
   clockThread2.start();
  }

 }
 
 public void stop(){
  clockThread1 = null;
  clockThread2 = null;
 }
 
 public void run(){
  while(Thread.currentThread() == clockThread1){
   color = Color.green;
   repaint();
   try{
    Thread.currentThread().sleep(1000);
   } catch (InterruptedException e){}
  }
 
  while(Thread.currentThread() == clockThread2){
   color = Color.blue;
   repaint();
   try{
    Thread.currentThread().sleep(1500);
   } catch (InterruptedException e){}
  }

 }
 
 public void paint(Graphics g){
  Calendar calendar = Calendar.getInstance();
  int hour = calendar.get(Calendar.HOUR_OF_DAY);
  int minute =calendar.get(Calendar.MINUTE);
  int second =calendar.get(Calendar.SECOND);
 
  g.setFont(font);
  g.setColor(color);
  g.drawString(hour + ":"+minute/10+minute%10+
            ":"+second/10+second%10, 10,60);

 }
}
 
 
 
Example - Stock Ticker


import java.awt.*;
import java.util.*;
import java.applet.*;

public class Ticker extends Applet implements Runnable{
 
 Thread clockThread1 = null, clockThread2 = null;
 Font font = new Font("Courier", Font.BOLD, 36);
 Color color = Color.green;

 String [] names = {"BBRC","RMBS","HON ","BOA ", "TNB ", "AT&T"};
 int [] prices =   {   94,   115,    35,   43,      20,    34};
 
 Dimension d;
 int x,y,decrement=2;
 Random num;
 boolean updated_prices = false;
 
 public void init(){
  d = getSize();
  x = d.width;
  y = font.getSize();
  num = new Random();
 }
 
 
 public void start(){
  if (clockThread1 == null){
   clockThread1 = new Thread(this);
   clockThread1.start();
  }
  if (clockThread2 == null){
   clockThread2 = new Thread(this);
   clockThread2.start();
  }

 }
 
 public void stop(){
  clockThread1 = null;
  clockThread2 = null;
 }
 
 public void run(){
  // This thread calls the paint() method to display stocks
  while(Thread.currentThread() == clockThread1){
   repaint();
   try{
    Thread.currentThread().sleep(100);
   } catch (InterruptedException e){}
  }
 
  //This thread updates the stock prices with a random
  // increment or decrement
  while(Thread.currentThread() == clockThread2){
   int change;
   int up_down;
   for(int i=0;i<prices.length;i++){
    change = num.nextInt()%3;
    up_down = num.nextInt()%2;
    if(up_down==0)
     change = -change;
    prices[i] += change;
   }
   updated_prices = true;
 
   try{
    Thread.currentThread().sleep(1000);
   } catch (InterruptedException e){}
  }
 }
 
 public void paint(Graphics g){
 //The following strings contain all the stock symbols
 // and stock totals for display by paint() method
 String symbols = new String();
 String tots = new String();
 
  g.setFont(font);
  FontMetrics fm =g.getFontMetrics();
 
  for(int i =0;i<names.length;i++)
   symbols += names[i]+"    ";
 
  for(int i =0;i<prices.length;i++){
    tots += "    "+ prices[i];
    if (prices[i]<100 && prices[i]>0)
     tots += "  ";
    else
     tots += " ";
  }

 
  // calculte total length of string
  int length = fm.stringWidth(symbols);
 
  //draw region with symbols
  g.setColor(Color.black);
  g.fillRect(0,0,d.width,y);
  g.setColor(Color.white);
  g.drawString(symbols,x,y);
 
  // draw region with prices
  g.setColor(Color.black);
  g.fillRect(0,y,d.width,y);
  if(updated_prices)
   g.setColor(Color.blue);
  else
   g.setColor(Color.green);
  g.drawString(tots,x,y+y-10);
 
  updated_prices = false;
 
  //move text one unit to left for next frame
  x -= decrement;
  // if text is completely off to left
  // move position back to right
  if(x < -length) x = d.width;
 }
}

 
Including sound files

Example


The following program illustrates the basic way of playing sound files.
 

Controlling sound playing
Method Description
soundclip.play() Plays the sound clip
soundclip.stop() Stops the sound clip
sound.loop() Plays the sound clip repeatedly.

Example


The following program displays two buttons - play, and stop. It plays music, and you can control playing or stopping music using buttons.

  import java.awt.*;
import java.applet.*;
import java.awt.event.*;
import java.net.*;

public class Sound2 extends Applet implements ActionListener{

Button play = new Button("play");
Button stop = new Button("stop");
Panel panel = new Panel();
TextField textbox = new TextField(20);
AudioClip soundclip;

public void init(){

play.addActionListener(this);
stop.addActionListener(this);
panel.setLayout(new GridLayout(1,2,20,20));
panel.add(play);
panel.add(stop);
add(panel);
add(textbox);
soundclip = getAudioClip(getDocumentBase(),"mozart.mid");
}

public void actionPerformed(ActionEvent event){

if(event.getActionCommand().equals("stop")) { soundclip.stop();
textbox.setText("soundclip stopped");
}
if(event.getActionCommand().equals("play")){ soundclip.play();
textbox.setText("soundclip playing");
}
}// end of actionPerformed()
}