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();
}
| 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 |
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);
}
}
| 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' |
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
}
<APPLET CODE="ClassName.class" WIDTH="nnn" HEIGHT="nnn"> </APPLET>
<HTML> <H3>My very first Applet</H3> <HR> <CENTER> <APPLET CODE="AddingMachine.class" WIDTH="300" HEIGHT="150"> </APPLET> </CENTER> </HTML>
appletviewer AddTest.html
| the input/output textfields are "too large" | |
| the quit button does not "work" |
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. |
| 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. |
| When applet initializes: |
| public void init() |
| public void stop() |
| public void start() |
| public boolean action(Event e, Object arg) |
| public boolean handleEvent(Event e) |
| public void repaint() |
| public void paint(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.
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
| 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 |
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.
![]()
![]()
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.
![]()