BohrAnim Applet Source Code
//BohrAnim -- A controllable animation by David L. Zellmer, June 27, 2000
//on 6/27/2000, after much reading, some solutions will be attempted using
//threadname.suspend() and threadname.resume()
//It Works! But I've read somewhere that use of these methods can lead to problems
//and other ways should be found to make threads come and go "naturally."
Begin
• import Java classes needed for applet
import java.awt.*;   //just load the whole Abstract Window Toolkit (awt) class this time
Declare a new BohrAnim class
• extension of the Applet class
• declare Runnable interface for threads.
• Runnable contains the run() method
• this class uses run() to control a thread.
//Set up the animation to use Threads by adding implements Runnable
//Use of threads allows the applet to run concurrently with other tasks
public class BohrAnim extends java.applet.Applet
   implements Runnable {
Declare global variables
• image array and image position variables
• "currentimage" is image shown
• "runner" declared the animation thread.
• initial value for animation frame delay
   Image bohrpix[] = new Image[9]; //set up an array to hold the images
   Image currentimage;
   int xpos = 10;    //ULHC of the image
   int ypos = 10;
   Thread runner;    //runner is a variable used to control the thread.
               //runner is really an object which will be declared an instance
               //of the class Thread down in the start() method below that will
               //actually start the thread running.
               
   int delay = 500; //frame delay in milliseconds
   
Declare global variables
• declare a Panel for controls
• scrollbar and button control variables
• "debug" for messages while testing
   //add these initial variable declarations for the controls
      Panel thePanel;            //The panel for the controls
      Scrollbar delayScrollbar;   //Scrollbar for the frame delay
      Button runButton;         //define button variables
      Button pauseButton;
      Button stepButton;
      Label delayLabel;         //Label variable to display the value of delay.
      
      boolean running = true;      //track if runner thread suspended or not.
      int nowstep = 0;         //used for stepping
      boolean debug = false;      //turn console printouts on or off. Used for debugging.
init() method
• executes first when applet is called
• loads the animation frames
• initializes scrollbar and buttons
• calls start().
   
   public void init() {
      //load the names into a string array
      String bohrnames[] = {"bohr1.gif" , "bohr2.gif" ,
      "bohr3.gif" , "bohr4.gif" , "bohr5.gif" , "bohr6.gif" ,
      "bohr7.gif" , "bohr8.gif" , "bohr9.gif" };
      //load the images into the image array
      for (int i=0; i < bohrpix.length; i++) {
         bohrpix[i] = getImage(getCodeBase(), "pix/" + bohrnames[i]);
         }
         
      //set up the control panel along the bottom border of the applet window
      thePanel = new Panel();
      setLayout(new BorderLayout());
      add("South", thePanel);
      thePanel.setLayout(new GridLayout(1,5,5,5));
      //init the scrollbar
      //arguments are (orientation, initial value, width of scrollbar,
      // minimum, maximum value)
      //in the Mac OS the width is fixed, so just enter zero.
      delayScrollbar = new Scrollbar(Scrollbar.HORIZONTAL, 500, 0, 0, 2000);
   
      //add the panel components
      delayLabel = new Label("Frame Delay 500");
      thePanel.add(delayLabel);
      thePanel.add(delayScrollbar);
   
      runButton = new Button("Run");
      thePanel.add(runButton);
      pauseButton = new Button("Pause");
      thePanel.add(pauseButton);
      stepButton = new Button("Step");
      thePanel.add(stepButton);
   }
   
start() method
• starts the animation thread
• called by init().
• not the same as runner.resume
• final thread termination done by stop()
   //boilerplate statement to start the thread when the applet loads
   public void start() {
      if (runner == null) {
         runner = new Thread(this);
         runner.start();
      }
   }
      
stop() method
• stops the animation thread for good
• cleans up when the applet is exited
• not the same as runner.suspend
   //boilerplate statement to stop the thread when the page is not viewed.
   public void stop() {
      if (runner != null) {
         runner.stop();
         runner = null;
      }
   }
      
run() method
• body of the animation thread.
• runs the frame display loop
• initially called by start() method.
• not the same as runner.resume
• paused by runner.suspend
• final thread termination done by stop()
   //run() is called in start() above and runs the animation thread "runner".
   //run() is a property of Runnable
   //the loop is paused or resumed by runner.suspend() or runner.resume()
   //in the methods called by the buttons.
   public void run() {
      setBackground(Color.white);
      running = true;
      while (true) {
         for (int i = 0; i < bohrpix.length; i++) {
            currentimage = bohrpix[i];
            repaint();
            pause(delay);   //see below for this method definition.
         }
      }
   }
pause(time) method
• makes the animation thread "sleep"
• time parameter in milliseconds
• try/catch finds safe time to sleep
         
   //this pause statment declares the delay between frames
   //the try catch statement is more boilerplate.
   public void pause(int time) {
      try {Thread.sleep(time);}
      catch (InterruptedException e) { }
   }
   
paint() method
• called indirectly by repaint()
• repaint() calls update()
• update() calls paint().
• declares the Graphics object "g"
• g.methods are used to draw the images.
   //repaint() used above calls this paint statement to draw the new image
   //repaint() takes care of restoring the background
   public void paint(Graphics g) {
      //draw image at xpos, ypos
      if (currentimage != null)
      g.drawImage (currentimage, xpos, ypos, this);
   }
   
update() method
• overrides screen clearing before a paint()
• reduces animation flicker.
   //try reducing flicker by overriding update() to not clear the screen between
   //frames. I will have to see if everything gets overpainted in the new frame.
   //It worked! But I did have to re-save my transparent gifs with a white background.
   //Override Update()
   public void update(Graphics g) {
      paint(g);
   }
   
handleEvent() method
• determines if scrollbar is used
• gets frame delay time from scrollbar
   //add in this event handler. This one handles only the scrollbar.
   //I used separate handlers for the buttons. It seems to work OK.
   public boolean handleEvent(Event event) {
      if (event.target instanceof Scrollbar) {
         delay = delayScrollbar.getValue();
         delayLabel.setText("Frame Delay " + String.valueOf(delay));
         repaint();      //I wonder if this is needed to repaint the scrollbar?
         return true;
      }
      return super.handleEvent(event);   //is this needed? It works.
   }
   
action() method
• determines if a button has been pressed
• calls the handleButtons() method
   //this method determines which of the buttons are pressed, then passes
   //the information along to the appropriate handler method found below.
   //note that the other event handler used for the scrollbar does not pass information
   public boolean action(Event evt, Object arg) {
      if (evt.target instanceof Button) {
         handleButtons((String) arg);
         return true;
      }
      return super.action(evt, arg);
   }
 
handleButtons(name) method
• called by action()
• determines which button is pressed
• calls the appropriate method
   //now for the button handler itself. I picked this name.
   //in the example of TYJM they did not declare this public
   
   public void handleButtons(String bname) { //bname will be local to this method.
      if (bname.equals("Run")) {
         
         delayLabel.setText("Frame Delay " + String.valueOf(delay));
         if (debug) System.out.println("Run has been pressed.");
         runanim();               //6/27/2000 call runanim()
      }
      else if (bname.equals("Pause")) {
         //this suspends the running animation.
         
         if (debug) System.out.println("Pause has been pressed.");
         pauseanim();            
      }
      else if (bname.equals("Step")) {
                  //stops the running animatio
         if (debug) System.out.println("Step has been pressed.");
         stepanim();
         }
      repaint();                  //just put this in as act of desperation.
   }
   //the following are the new methods that execute with each button
   
runanim() method
• press "Run" button to execute
• called by handleButtons()
• makes the animation loop indefinitely
   public void runanim() {
      if (debug) System.out.println("In runanim.");         
      delayLabel.setText("Frame Delay " + String.valueOf(delay));
      if (!running) {
         runner.resume();
         if (debug) System.out.println("Runner resumed.");
         running = true;
      }
      repaint();
   }
stepanim() method
• press "Step" button to execute
• called by handleButtons()
• steps animation one frame
   public void stepanim() {
      if (debug) System.out.println("In stepanim.");
      if (running) {
         runner.suspend();
         if (debug) System.out.println("Runner suspended.");
         running = false;
      }
      if (nowstep >= bohrpix.length) nowstep = 0;
         currentimage = bohrpix[nowstep];
         if (debug) System.out.println("nowstep = " + nowstep);
         nowstep = nowstep + 1;
         repaint();
   }
 
pauseanim() method
• press "Pause" button to execute
• called by handleButtons()
• stops animation at current frame
   
   public void pauseanim() {
      if (debug) System.out.println("In pauseanim.");
      if (running) {
         runner.suspend();   //suspend runner thread.
         if (debug) System.out.println("Runner suspended.");
         running = false;
      }
      delayLabel.setText("Frame Delay " + String.valueOf(delay));
      if (debug) System.out.println("Pause button pressed.");
   }
 
 
}
This is the end of the applet.