CIS 121 March 13, 2000


Applets

Recall that an applet is a special type of Java program. It has no main method, and, therefore, cannot be executed with the Java interpreter. Applets are run in the context of a web browser or the appletviewer which is included with the JDK from Sun Microsystems. There are a few methods that are particular to applets that need to be mentioned.

init This method can be used the way a constructor is used. (We can have a default constructor in the applet if we want) This method is executed once when the browser/appletviewer loads the applet.
start This method is called each time the applet is made visible. That is, if we minimize the browser, then maximize it again so that the applet is visible, the start method is executed again. However, init is not executed again.
stop This method is called when the browser/appletviewer changes so that the applet is no longer visible.
destroy This method is called when the browser/appletviewer changes so that the applet will stop running completely. For example, if we exit the browser/appletviewer. It is similar to the finalize method in a normal program.

Within an applet there is a special method that we can take advantage of.

The method repaint will cause the method paint to be called as soon as the browser/appletviewer can call it. This is why it is important not to place code in the start or init methods which take a long time to execute especially infinite loops.

Applets cannot run by themselves since they have no main method. They must be run in the context of a browser which acts as the parent of the applet. In order to run an applet we must write a small HTML (HyperText Markup Language) document. We do not need a complete HTML document. The following code will suffice. Suppose we wish to run the applet MyApplet.class.

<applet code=MyApplet width=w height=l>
</applet>

Threads

Recall that a thread is a single sequential flow of control. In all of the programs that you wrote in CIS 120 and this the first part of this semester ran in their own thread. Java allows us to have more than one thread executing simulataneously. Since the CPU can only process one thing at a time, multithreading is accomplished by very quickly switching threads in and out of the processor.

Multithreading is sometimes necessary when developing applets. Recall that an applet cannot have a main method, and, therefore, must be run in the context of a browser or appletviewer. The browser/appletviewer runs in its own thread, and so we must make sure that the methods in the applet do not take a long time to execute. In particular, we shouldn't have infinite loops in the methods of an applet if we intend to have that applet run in the thread of the browser/appletviewer.

The following is a partial sequence of execution when an applet runs in the thread of a browser/appletviewer.

The browser/appletviewer loads the applet
The init() method in the applet is executed
The start() method in the applet is executed

The class Thread is found in the java.lang. Since each applet we create extends the class Applet, we can't make the applet a subclass of Thread. In order to run an applet in its own thread we implement the interface Runnable. The Runnable interface has one method which must be overridden, public void run(). This method is run when the thread is started.

There a few methods available in the Thread class that are important. Suppose we have a Thread object called thread.
thread.start() causes the thread to begin executing. The run() method is then called.
thread.suspend() temporarily halts the execution of the thread.
thread.resume() resume execution of the thread
thread.stop() permanently stops the execution of the thread

There is a static method called sleep in the class Thread which takes as an argument a number of milliseconds. We can use this method to cause the Thread to sleep for a number of milliseconds. This method must be called in a try block since it might cause an InterruptedException to be thrown. This occurs if the user halts the applet while the thread is sleeping.

Let us look again at examples of using threads.

Recall the digital clock example.
import java.applet.*;
import java.awt.*;
import java.util.*;

public class DigitalClock extends Applet {
   Font font = new Font("Monospaced",Font.BOLD,16);
   int hours,mins,secs;

   public void start() {
      while (true) {
         Calendar time = Calendar.getInstance();

         hours = time.get(Calendar.HOUR);
         mins = time.get(Calendar.MINUTE);
         secs = time.get(Calendar.SECOND);

         repaint();
      }
   }

   public void paint(Graphics g) {
      g.setFont(font);
      g.drawString(String.valueOf(hours) + ":" +
                   String.valueOf(mins) + ":" +
                   String.valueOf(secs),50,50);
   }
}
Since we don't override the init method nor have a constructor, the first method executed by the browser/appletviewer is the start method. Within the start method we have an infinite loop which tries to call the repaint method. Recall that a call to the repaint method will cause the browser/appletviewer to call the method paint as soon as it can. However since we have an infinite loop in the start method, the browser/appletviewer never regains control so nothing happens.

In order to correct the problem, we run the applet in its own thread. We will override the init method to start the thread running.
import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class DigitalClock2 extends Applet implements Runnable {
   Font font = new Font("Monospaced",Font.BOLD,16);
   int hours,mins,secs;
   Thread appletThread;

   public void init() {
      if (appletThread == null) {
         appletThread = new Thread(this);
         appletThread.start();
      }
   }   

   public void run() {
      while (true) {
         Calendar time = Calendar.getInstance();

         hours = time.get(Calendar.HOUR);
         mins = time.get(Calendar.MINUTE);
         secs = time.get(Calendar.SECOND);

         repaint();

         try {
            Thread.sleep(1000);
         } catch (InterruptedException i) {
            System.exit(1);
         }
      }
   }

   public void destroy() {
      if (appletThread != null) {
         appletThread.stop();
         appletThread = null;
      }
   }

   public void paint(Graphics g) {
      g.setFont(font);
      g.drawString(String.valueOf(hours) + ":" +
                   String.valueOf(mins) + ":" +
                   String.valueOf(secs),50,50);
   }
}


Another important aspect in this applet is how the time is calculated. In the package java.util is the class Calendar. In order to get the current time, we make a new instance of a calendar object by using the method getInstance. When the method getInstance is called we have available to use the time of the system at the time the call was made. In order to access the time, we use the get method. Get accepts as a parameter an integer indicating what field we would like. There are several static constants we can make use of in the Calendar class, in particular HOUR, MINUTE, SECOND, and HOUR_OF_DAY. HOUR_OF_DAY will return the hour in 24-hour format, i.e. 5 pm will give an HOUR_OF_DAY of 17.

Each time we wish to make a time calculation we must make another call to getInstance.

Let us look at a slightly more complicated example of an applet.
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class Walker extends Applet implements Runnable,ActionListener,ItemListener {
   int        scale=1;
   Panel      drawingArea = new Panel(null);
   Thread     appletThread;
   Button     north;
   Button     west;
   Button     south;
   Button     east;
   Panel      messageArea = new Panel(null);
   TextField  message = new TextField();
   String     direction="South";
   int        x = 250;
   int        y = 0;
   Choice     speedChoice = new Choice();
   int        speed = 5;
   TextField  directionArea;

   public class MyException extends Exception {
      public String message;

      public MyException(String s) {
         super(s);

         message = s;
      }

   }

   public Walker() {
      setLayout(null);

      drawingArea.setSize(500,200);
      drawingArea.setLocation(0,0);

      Panel controlPanel = new Panel(new FlowLayout());

      Font font = new Font("Sanserif",Font.BOLD,12);

      Label directionLabel = new Label("Direction ");
      directionLabel.setFont(font);

      directionArea = new TextField("South",10);

      controlPanel.add(directionLabel);
      controlPanel.add(directionArea);

      north = new Button("North");
      west = new Button("West");
      south = new Button("South");
      east = new Button("East");

      controlPanel.add(north);
      controlPanel.add(west);
      controlPanel.add(south);
      controlPanel.add(east);

      Label speedLabel = new Label(" Speed ");
      speedLabel.setFont(font);
      
      speedChoice.add("5");
      speedChoice.add("10");
      speedChoice.add("15");
      speedChoice.add("20");
      speedChoice.add("25");
      speedChoice.add("30");
      speedChoice.add("35");
      speedChoice.add("40");
      speedChoice.add("45");
      speedChoice.add("50");

      speedChoice.addItemListener(this);

      controlPanel.add(speedLabel);
      controlPanel.add(speedChoice);

      north.addActionListener(this);
      west.addActionListener(this);
      south.addActionListener(this);
      east.addActionListener(this);

      controlPanel.setSize(500,50);
      controlPanel.setLocation(0,200);

      message.setSize(490,30);
      message.setLocation(5,10);

      messageArea.add(message);

      messageArea.setSize(500,50);
      messageArea.setLocation(0,250);

      add(drawingArea);
      add(controlPanel);
      add(messageArea);
   }

   public void init() {
      setBackground(Color.white);

      if (appletThread == null) {
         appletThread = new Thread(this);
         appletThread.start();
      }
   }

   public void run() {
      Graphics g = drawingArea.getGraphics();

      while (true) {
         if (direction.equals("South")) {
            try {
               y+=speed;
               if ((y+40*scale)>=195) {
                  y-=speed;
                  throw new MyException("You can't go any farther South. Please choose another direction.");
               }
            } catch (MyException exc) {
               System.out.println("Hello");
               message.setText(exc.message);
            }
            drawFigure(g,Color.white,x,y-speed);
            drawFigure(g,Color.black,x,y);
         }
         if (direction.equals("West")) {
            try {
               x-=speed;
               if ((x-20*scale)<=5) {
                  x+=speed;
                  throw new MyException("You can't go any farther West. Please choose another direction.");
               }
            } catch (MyException exc) {
               message.setText(exc.message);
            }
            drawFigure(g,Color.white,x+speed,y);
            drawFigure(g,Color.black,x,y);
         }
         if (direction.equals("North")) {
            try {
               y-=speed;
               if (y<=5) {
                  y+=speed;
                  throw new MyException("You can't go any farther North. Please choose another direction.");
               }
            } catch (MyException exc) {
               message.setText(exc.message);
            }
            drawFigure(g,Color.white,x,y+speed);
            drawFigure(g,Color.black,x,y);
         }
         if (direction.equals("East")) {
            try {
               x+=speed;
               if ((x+20*scale)>=495) {
                  x-=speed;
                  throw new MyException("You can't go any farther East. Please choose another direction.");
               }
            } catch (MyException exc) {
               message.setText(exc.message);
            }
            drawFigure(g,Color.white,x-speed,y);
            drawFigure(g,Color.black,x,y);
         }
         try {
            Thread.sleep(1000);
         } catch (InterruptedException e) {
         }
      }
  
   }

   public void paint(Graphics g) {
   }

   public void drawFigure(Graphics g,Color c,int x,int y) {
      g.setColor(c);
      g.fillRect(x-10*scale,y,20*scale,10*scale);
      g.fillRect(x-scale,y+10*scale,2*scale,10*scale);
      g.fillRect(x-20*scale,y+20*scale,40*scale,2*scale);
      g.fillRect(x-20*scale,y+22*scale,2*scale,8*scale);
      g.fillRect(x+18*scale,y+22*scale,2*scale,8*scale);
      g.fillRect(x-16*scale,y+22*scale,32*scale,18*scale);
      g.fillRect(x-16*scale,y+40*scale,2*scale,10*scale);
      g.fillRect(x+14*scale,y+40*scale,2*scale,10*scale);
   }

   public void actionPerformed(ActionEvent e) {
      direction = e.getActionCommand();
      directionArea.setText(direction);
   }

   public void itemStateChanged(ItemEvent e) {
      if (e.getStateChange() == ItemEvent.SELECTED) { 
         speed = (Integer.valueOf((e.getItem().toString()))).intValue();
         System.out.println(speed);
      }
   }

}
Make note of how the exceptions are handled.

Notes about the program. There are several things you must do in your programming assignment.
  1. Create two button objects and respond to button clicks.
  2. Create three checkbox objects and respond to checks.
  3. Draw a figure on the screen.
  4. Decide how long the figure will remain on the screen.
  5. Decide whether or not there is a hit.
  6. Create textbox components and place various text in them based on activities in the game.
  7. Create a timer.
  8. Implement Exception Handling.
In order to generate random numbers, we use the static method random in the Math class. The return type of random is a double. It returns a random value between 0 and 1.

In order to accomplish drawing, we have several methods available in the Graphics class.

public abstract void clearRect(int x,int y,int width,int height)
public abstract void drawLine(int xl,int yl,int x2,int y2)
public abstract void drawOval(int x,int y,int width,int length)
public void drawRect(int x,int y,int width,int height)
public abstract void drawString(String str,int x,int y)
public abstract void fillOval(int x,int y,int width,int height)
public abstract void fillRect(int x,int y,int width,int height)
public abstract void setColor(Color c)
public abstract void setFont(Font font)
public String toString()

You are free to draw the figure in any shape you want. An important question is how do you determine whether or not the figure has been hit. One way of answering the question is to assume that the gun is at the middle of the lower part of the applet in a fixed location. When you draw the figure, you must also randomly generated the amount of time that the figure will remain on the screen.

The criteria for the figure being hit are:
  1. The gun is aimed in the direction of the figure.
  2. The bullet has time to get to the figure.
In order to determine whether or not the gun is aimed in the direction of the figure, you may make use of methods from the Math class, in particular the arcsin method.