Applets and Graphics 

So far, we know how to create stand-alone programs that are based on a Frame. We have learned about several layout managers (FlowLayout, BorderLayout, GridLayout), and we have seen examples of GUI elements (Button, Label, TextField, TextArea). Now we will start creating applets, and intrudocue the last commonly used classes in the awt, Graphics and Canvas. We will also experiment more with mixing various layouts using the Panel class.

Applets

An applet is a small program that executes inside another program. In our case, every applet must extend the Applet class, which is defined by the awt as follows:
 

public class Applet extends Panel
{   // Constructors
    public Applet(); 
    // selected Methods
    public AppletContext getAppletContext();
    public String getAppletInfo();
    public AudioClip getAudioClip(URL  url, String  name);
    public URL getCodeBase();
    public URL getDocumentBase();
    public Image getImage(URL  url, String  name);
    public String getParameter(String  name);
    public void init();
    public boolean isActive();
    public void play(URL  url, String  name);
    public void showStatus(String  msg);
    public void start();
    public void stop();
}
This is certainly a lot of info, and many of these methods may sound intruiging. However, let's first concentrate on the basics:

Applet Basics:

An applet contains three important methods: init(), start(), and stop()
init() is called once, when the applet is loaded into memory
start() tells the applet to start execution; it is called every time a user visits the page containing a loaded applet
stop() tells the applet to stop execution; it is called every time a user leaves a page containing a started applet
An applet can not be resized. Applets are embedded into web pages, and they will get a fixed height and width. Layout managers will still try to arrange components inside an applet as best as possbible, but sometimes that can be difficult Applets do not need a constructor, and they do not need a main method to execute; the init and start methods can substitute for them Every class that wants to be an applet must import java.applet.Applet, and extend Applet An applet is initially visible. Therefore, the 'show()' method is not needed. Also, the 'pack()' method is not needed as well.
With this information in hand, let's redo our last 'simple' adding machine as an applet. Recall the last code for the adding machine:
import java.awt.*;

public class AddingMachine extends Frame
{  // fields
   private Button    add      = new Button("Add"),  
                     reset    = new Button("Reset"),
                     quit     = new Button("Quit");

   private TextField output   = new TextField(6),   
                     input    = new TextField(6);
   private Label     outLabel = new Label("Sum:"),  
                     inLabel  = new Label("Add:");
   private int       sum = 0;
   // constructor
   public AddingMachine(String t)
   {
      super(t);
      setup();
      pack();
      show();
   }
   // methods
   public boolean action(Event e, Object o)
   {
      if (e.target == quit)
         System.exit(0);
      else if (e.target == add)
      {
         sum += Integer.parseInt(input.getText());
         output.setText(String.valueOf(sum));
      }
      else if (e.target == reset)
      {
         sum = 0;
         output.setText("");
      }     
      return false;
   }
   private void setup()
   {
      Panel info    = new Panel();   // panel for the number and labels
      Panel buttons = new Panel();   // panel for the buttons

      info.setLayout(new GridLayout(2,2));  // methods of the info panel
      info.add(inLabel);   
      info.add(input);
      info.add(outLabel);  
      info.add(output);

      buttons.setLayout(new FlowLayout());  // methods of the buttons panel
      buttons.add(add);
      buttons.add(reset);
      buttons.add(quit);

      setLayout(new BorderLayout());       // methods of the frame
      add("South",buttons);       
      add("Center",info);
   }
}
Actually, we have also used different layouts for this applet, and the above code reflects the 'best looking' layout for our purpose. To turn this class into an applet, we need to do the following:
import java.applet.*
change the class from "extends Frame" to "extends Applet"
change the constructor method to "public void init()"
remove the constructor of the superclass (only constructors can call other constructors, but our method has just been renamed to 'init()', and is therefore no longer a constructor)
remove the method calls to 'pack' and 'show'
Here is the new code for our applet:
import java.awt.*;
import java.applet.*;

public class AddingMachine extends Applet
{  // fields - no change at all
   // constructor changed to init method
   public void init()
   {
      setup();
   }
   // no change in action and setup methods for now
}
That's it, we now have an applet that can be embedded into a Web page. Here's how:

Embedding an Applet into a Web page

To embed and applet into a web page, create an HTML document as usual. Then, at the place where you want the applet to appear, but in the following tags as a minimum:
<APPLET CODE="ClassName.class"
        WIDTH="nnn"
        HEIGHT="nnn">
</APPLET>
In other words, a very simple HTML document containing the above applet would look like this:
<HTML>
<H3>My very first Applet</H3>
<HR>
<CENTER>
<APPLET CODE="AddingMachine.class"
        WIDTH="300"
        HEIGHT="150">
</APPLET>
</CENTER>
</HTML>
Save this file, using any name, say, "AddTest.html",  in the same directory as the class file for the AddingMachine applet, and load it into your web browser. You should see the AddingMachine applet inside your web page.
Alternatively, you can also use a small program called 'appletviewer' to view an applet quickly. The appletviewer comes with the JDK, and it ignores all HTML code except the <APPLET> tags. It can be used to quickly check whether an applet is working correctly. To use it, simply type, at the command line:
appletviewer AddTest.html

Applet Details

When you test the above applet, you will notice at least two things:
the input/output textfields are "too large"
the quit button does not "work"
The first problem is due to the fact that an applet can not automatically resize itself. It will simply try to fit into the space allocated as best as possible. Therefore, you sometimes have to adjust the HEIGHT and WIDTH fields of the applet tag for a while before finding the optimal dimensions.
The second problem is due to "security restrictions" on applet. Applets have several restrictions - things that applets are not allowed to do. One such restriction is that an applet is not allowed to call the System.exit() method. In fact, if you bring up the Java console (under the Options menu), you will see that a security violation appears every time you press the "Quit" button.
Finally, in every applet you should think about using the start and/or stop methods, if applicable. In our case, we could use the 'start' method to reset the computations every time a user visits this page.
Here is the new code, with the "Quit" button removed, and the Start method added. For convenience, the entire code is listed again.
import java.awt.*;
import java.applet.*;

 public class AddingMachine extends Applet
 {  // fields
    private Button    add      = new Button("Add"),  
                      reset    = new Button("Reset");
    private TextField output   = new TextField(6),   
                      input    = new TextField(6);
    private Label     outLabel = new Label("Sum:"),  
                      inLabel  = new Label("Add:");
    private int       sum = 0;
    // init method
    public void init()
    {
       setup();
    }
    // public methods
    public void start()
    {
       boolean dummy = handleReset();
    }
    public boolean action(Event e, Object o)
    {
       if (e.target == add)
          return handleAdd();
       else if (e.target == reset)
          return handleReset();

       else
          return true;
    }
    // private methods
    private boolean handleAdd()
    {
       sum += Integer.parseInt(input.getText());
       output.setText(String.valueOf(sum));
       return true;       
    }
    private boolean handleReset()
    {
       sum = 0;
       output.setText("0");
       input.setText("0");
       return true;
    }     
    private void setup()
    {
       Panel info    = new Panel();   // panel for the number and labels
       Panel buttons = new Panel();   // panel for the buttons

       info.setLayout(new GridLayout(2,2));  // methods of the info panel
       info.add(inLabel);   
       info.add(input);
       info.add(outLabel);  
       info.add(output);

       buttons.setLayout(new FlowLayout());  // methods of the buttons panel
       buttons.add(add);
       buttons.add(reset);

       setLayout(new BorderLayout());       // methods of the frame
       add("South",buttons);       
       add("Center",info);
    }
}

Note: In addition to removing the "Quit" button and adding the start() method, we have done several other changes:

We have removed the "Quit" button as a field, in the action method, and in the setup method
We have added a 'public void start()' method. That method is automatically called every time the page is revisited. It means that if you look at the page with the applet, do a few calculations, switch to another page, then return to the page with the applet, the computations have automatically been reset.
We have added two private methods handleReset and handleAdd. Those methods are called by the action method when the appropriate buttons are called. That's good programming practice. For one thing, the handleReset method is called when the 'Reset' button is pressed, and when the 'start' method is executed (automatically every time you re-visit the page with this applet). We can now change the handleReset methd (which indeed we did) only once and the new code will apply at both places. In general, it is always good to create small method for individual tasks. In other words, rather than letting the 'action' method do a lot of work, it should rather delegate the work to other methods, depending on what action happens. The actual work should take place in - usually - private methods.
We have changed what the action method returns. It now returns true only if it actually handles the event, false otherwise. That's good programming practice. The action method should only return true when it actually handles an event. It should return false otherwise. An even better way to do it is to have the action method return 'super.handleEvent(e, o)' if it does not actually handle the event. That gives the action method of the superclass a chance to handle the event, if necessary.
Note that our private methods 'handleAdd' and 'handleReset' now return the boolean variable true. That allows us to shorten the code of the action method, but it forces us to add a dummy variable when calling the 'handleReset' method in the 'start' method. That's no big loss, and the shortening of the action method more than makes up for the introduction of one dummy boolean variable in the start method.
From now on we will concentrate on applets, but it should be clear how to convert an applet into a standalone program, if necessary.  To change an Applet to a Frame, simply reverse the above steps of turning a frame into an applet, i.e.:
remove the "import java.applet.*" line
make sure your class extends Frame rather than Applet
change the 'public void init()' method to a constructor
add a call to the superclass constructor, if necessary, and calls to 'pack()' and 'show()', if necessary, to show your applet
for Frames, the 'start()' and 'stop()' methods are no longer executed automatically. You can usually leave them as they are, but they do not serve any purpose any more. They are only called automatically for applets.
Add a way to quit the program, for example a "Quit" button. Make sure that you call the System.exit(0) method inside the action method if a user clicks on that Quit button. To do that nicely, you should introduce a private method 'public boolean handleQuit()', similar to the other private 'handleWhatever' methods.

Drawing Graphics

 Next, we want to discuss how to draw graphics inside a program or applet. Before drawing graphics, we need to understand what methods are, by default, executed for the basic applet. We already know about init, start, and stop, but there's a little more:
When applet initializes:
public void init()
When leaving page with applet
public void stop()
When entering page with previously initialized appet
public void start()
When an action event happens:
public boolean action(Event e, Object arg)
When another event happens:
public boolean handleEvent(Event e)
When the applet needs updating:
public void repaint()
When an applet calls repaint()
public void paint(Graphics g)
We have already seen most of these methods except the repaint() and the paint() method. What the repaint() method will do is, to quote the Java API:
"Repaints this component. This method causes a call to this component's update method as soon as possible."
Thus, to understand this, we need to consult the Java API about the update method and we find:
 public void update(Graphics g)

Updates this component. The AWT calls the update method in response to a call to repaint. The appearance of the component on the screen has not changed since the last call to update or paint.

The update method of Component:

        1.Clears this component by filling it with the background color.
        1.Sets the color of the graphics context to be the foreground color of this component.
        2.Calls this component's paint method to completely redraw this component.
          The  coordinate of the graphics context is the top-left corner of this components.
          The clipping region of the graphics context is the bounding rectangle of this
          component
          Parameters:
               g - the graphics context to use for updating

So, to finish this, we need to look at the paint method, which is defined as:
public void paint(Graphics g)
 
Paints this component. Most application components, including applets, override this method.
The  coordinate of the graphics context is the top-left corner of this components. The clipping region of the graphics context is the bounding rectangle of this component.
What does this mean: it simply means that if we want to do any graphics, we can simply override the object's paint method and handle all drawing in that method. That will then automatically ensure that our graphics always appears and is redrawn whenever necessary. That works, because the 'repaint()' method is called automatically if an applet (or frame) needs updating. That method in turn automatically calls 'update', and that method clears the drawing area and automatically calls 'paint'. If we override 'paint', therefore, our drawing happens automatically, at the proper time. Here is, for example, our applet from the very beginning of class:
import java.applet.Applet;
import java.awt.*;

public class HelloWorld extends Applet
{
   public void paint(Graphics g)
   {
      g.drawString("Hello World", 10, 10);
   }
}
This applet will draw the string Hello Word, starting at x-coordinate 10 and y-coordinate 10 inside the applet, where the top left corner has coordinates (0,0). Of course we need to create an appropriate HTML file before we can see this applet inside a web page, or using the appletviewer.
 
For more details on drawing graphics, here's a look at selected methods of the Java API on the Graphics class:
public void draw3DRect(int  x, int  y, int  width, int  height, boolean  raised);
public abstract void drawLine(int  x1, int  y1, int  x2,  int  y2); 
public abstract void drawOval(int  x, int  y, int width, int  height); 
public abstract void drawPolygon(int  xPoints[], int yPoints[], int  nPoints); 
public void drawPolygon(Polygon  p); 
public void drawRect(int  x, int  y, int  width, int  height); 
public abstract void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight); 
public abstract void drawString(String  str, int  x, int  y); 
public void fill3DRect(int  x, int  y, int  width, int  height, boolean  raised); 
public abstract void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle); 
public abstract void fillOval(int  x, int  y, int  width, int  height); 
public abstract void fillPolygon(int  xPoints[], int  yPoints[], int  nPoints); 
public void fillPolygon(Polygon  p); 
public abstract void fillRect(int  x, int  y, int  width,  int  height); 
public abstract void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight); 
public abstract Color getColor(); 
public abstract Font getFont(); 
public FontMetrics getFontMetrics(); 
public abstract FontMetrics getFontMetrics(Font  f); 
public abstract void setColor(Color  c); 
public abstract void setFont(Font  font);
Now let's try to design a slightly more interesting applet:

Example:

We want to design an applet that will draw a rectangle in some color on the screen. It will also have four buttons, up, down, right, left. You can then move the rectangle with these buttons in the corresponding direction. We'll also add two more buttons to increase or decrease the size of the rectangle.
Let's start out easy, i.e. without worrying about these buttons, and moving the rectangle. Let's just try to draw it at some specific coordinages, at a specific size. We therefore need, as fields:
x and y coordinates for the location of the rectangle
width and height numbers for the size of the coordinates
a field of type Color for the color of the rectangle
Here's the code:
import java.applet.Applet;
import java.awt.*;

public class MoveBox extends Applet
{
   private int x = 20, y = 20;
   private int width = 10, height = 10;
   private Color myColor = Color.red;

   public void paint(Graphics g)
   {
      g.setColor(myColor);
      g.fillRect(x,y,width,height);
   }
}

Note that at this point, we do not need the 'init', 'start', or 'stop' method; the fields are initialized as they are declared, and nothing changes anyway, so no need for those methods right now. To test this, don't forget to create the appropriate HTML file.

 Next, let's add the four buttons to move the rectangle around. That will mean that we also need to implement the 'init' method, to choose a layout for those buttons. Finally, we will introduce an extra field to specify how much the retangle should move with each click on a button.

import java.applet.Applet;
import java.awt.*;

public class MoveBox extends Applet
{
   private int x = 20, y = 20;
   private int width = 10, height = 10;
   private int inc = 5;
   private Color myColor = Color.red;

   private Button up = new Button("Up");
   private Button down = new Button("Down");
   private Button left = new Button("Left");
   private Button right = new Button("Right");
   // init method
   public void init()
   {
      Panel buttons = new Panel();
      buttons.setLayout(new FlowLayout());
      buttons.add(up);
      buttons.add(down);
      buttons.add(left);
      buttons.add(right);

      setLayout(new BorderLayout());
      add("South", buttons);
   }
   // public methods
   public boolean action(Event e, Object o)
   {
      if (e.target == up)
         return handleUp();
      else if (e.target == down)
         return handleDown();
      else if (e.target == left)
         return handleLeft();
      else if (e.target == right)
         return handleRight();
      else
         return super.action(e, o);
   }
   public void paint(Graphics g)
   {
      g.setColor(myColor);
      g.fillRect(x,y,width,height);
   }
   // private methods
   private boolean handleUp()
   {
      y -= inc;
      repaint();
      return true;
   }
   private boolean handleDown()
   {
      y += inc;
      repaint();
      return true;
   }
   private boolean handleRight()
   {
      x += inc;
      repaint();
      return true;
   }
   private boolean handleLeft()
   {
      x -= inc;
      repaint();
      return true;
   }
}

Note that we use the private 'handleWhatever' methods, following the Good Programming Practice hints we mentioned earlier.

Finally, for our finished applet so far, we should add two more buttons to change the size of the rectangle. But, you may have also noticed a flaw in our applet: the rectangle can now disappear when moving it passed the borders of the applet. We should modify our code so that the rectangle appears on the left side if it is moved passed the right side, and on the right side if it is moved passed the left side. For that, we need to know, inside the applet, the width and height of the applet. A call to the 'super.height' and 'super().width' fields will reveal those numbers. Here is the new code.

import java.applet.Applet;
import java.awt.*;

public class MoveBox extends Applet
{
   private int x = 20, y = 20;
   private int width = 10, height = 10;
   private int inc = 5;
   private Color myColor = Color.red;

   private Button up = new Button("Up");
   private Button down = new Button("Down");
   private Button left = new Button("Left");
   private Button right = new Button("Right");
   private Button increase = new Button("[+]");
   private Button decrease = new Button("[-]");

   // init method
   public void init()
   {
      Panel buttons = new Panel();
      buttons.setLayout(new FlowLayout());
      buttons.add(up);
      buttons.add(down);
      buttons.add(left);
      buttons.add(right);
      buttons.add(increase);
      buttons.add(decrease);

      setLayout(new BorderLayout());
      add("South", buttons);
   }
   // public methods
   public boolean action(Event e, Object o)
   {
      if (e.target == up)
         return handleUp();
      else if (e.target == down)
         return handleDown();
      else if (e.target == left)
         return handleLeft();
      else if (e.target == right)
         return handleRight();
      else if (e.target == increase)
         return handleIncrease();
      else if (e.target == decrease)
         return handleDecrease();
      else
         return super.action(e, o);
   }
   public void paint(Graphics g)
   {
      g.setColor(myColor);
      g.fillRect(x,y,width,height);
   }
   // private methods
   private boolean handleUp()
   {
      y = y - inc;
      repaint();
      return true;
   }
   private boolean handleDown()
   {
      y = y + inc;
      repaint();
      return true;
   }
   private boolean handleRight()
   {
      if (x < size().width)
         x = x + inc;
      else
         x = 0;
      repaint();
      return true;
   }
   private boolean handleLeft()
   {
      if (x > 0)
         x = x - inc;
      else
         x = size().width;
      repaint();
      return true;
   }
   private boolean handleIncrease()
   {
      width += 5;
      height += 5;
      repaint();
      return true;
   }
   private boolean handleDecrease()
   {
      if (width > 5)
      {
         width -= 5;
         height -= 5;
      }
      repaint();
      return true;
   }
}

Note that have made sure that the size of the rectangle can not shrink too much. Is it clear how the entire code works, especially the 'wrapping' around the left and right sides ? Actually, to be picky, the 'wrapping' does not work quit perfectly. On the right side, it wraps 'too late', and on the 'left side, it 'wraps' too early. Try it to see what I mean.

Your browser is not Java-enabled. Get Netscape Communicator 4.0

Actually, we should also change it so that the rectangle can 'wrap' around the top and buttom borders as well - we will leave that up to you to try for yourself. While you are at it, also add two addition buttons, and the necessary code, to make the rectangle move faster or slower when clicking on the apppropriate buttons. That, again, is left to you.

(bgw)