All right, so by now you're drawing on the screen, and wondering when you are going to write the next killer game and be a multizillionaire.
That may take a while.
However, we can move on to the next part of the process. As we mentioned before, the primary aspect of applets and applications is the loop structure, you check for events, make any computations you need, and redraw the screen.
You've learned how to draw to the screen, now let's work on how to make this more interactive. In order to do that, we need some controls, and we need to know how to respond to those controls.
Laying out the applet
To have both controls and a drawing area, we need to partition the applet into two parts, one which contains the controls, and one which contains a drawing area. The Java AWT has a class designed for holding other screen objects called a Panel. The Panel is what you will use to place your controls.
A second object is available for the purposes of drawing called a Canvas.
The following code creates a screen which contains three Buttons, which will eventually change the color of the Canvas (after we add some functionality).
import java.awt.*;
import java.applet.*;
public class ColorChange extends Applet{
Panel rightside; //Panel to hold controls
Canvas leftside; //Canvas for drawing
Button red; //first button
Button blue; //second button
Button green; //third button
public void init(){ //screen set up
//in init routine
setLayout(new GridLayout(1,2)); //main applet
//split into two
//parts
leftside = new Canvas(); //Canvas instantiated
rightside = new Panel(); //Panel instantiated
leftside.setSize(getSize().width/2,getSize().height);
rightside.setSize(getSize().width/2,getSize().height);
//sizes are set to half the width of the screen
//but the whole height.
add(leftside); //elements are added to the applet
add(rightside);
rightside.setLayout(new GridLayout(3,1));
//control panel is divided into 3 parts
rightside.add(red = new Button("Red")); //add buttons
rightside.add(blue = new Button("Blue")); //to panel
rightside.add(green = new Button("Green"));
}
}
OK, so what does this applet do? We introduce three special classes from the AWT toolkit: Panels, Canvases, and Buttons.
Both the applet and the Panel have their layout set with the GridLayout layout manager. What layout managers do is decide where to put stuff on the screen, and how big they should be. Check the AWT reference for more on the different layout managers. The GridLayout is perhaps the easiest to use, as it simply breaks up an object into an evenly spaced grid.
Variable scope
Notice that many of the variables are declared inside of the class, but not inside of the init() routine. Where a variable is declared tells the computer what scope the variable has, that is, who understands what that variable is. When you define a variable inside of a function, that variable cannot be used outside of the function. By declaring many of the variables at the same level as the routines that will be called in the ColorChange class, we ensure that all routines will have access to them
Applet layout: The init() routine
One of the functions you can override in an applet is the init() routine, which is called to set the layout of the applet. We use one of the most common and simple to use layouts for this applet, the GridLayout, which breaks up the applet into an evenly spaced grid. We will see in the next lesson that we can use panels within panels to further subdivide an applets layout, making robust designs with this simple layout manager.
Notice that for this applet, we split the screen into two halves. On the left side of the applet, we place a Canvas, which is a class in AWT designed for a drawing space. On the right side of the applet, we place a Panel, which is a special class designed to hold other classes. We subdivided the panel into 3 parts, and add 3 Buttons to the panel.
Compile this applet, create a web page for it, and view it in your favorite browser.
Creating a drawing space
Notice that as you click the buttons, currently nothing happens. We still need to do two things, first, we need to set up the drawing area, and second, we need to add event handling.
So how do we set up the drawing area? We mentioned earlier that a Canvas would be used, but very little other than that was said. The reason for this is that we don't use the Canvas directly, but rather we create our own version of a Canvas, with our own special instructions.
This is called class inheritance. To create our own kind of canvas, in the file CanvasObject.java, enter the following code.
import java.applet.*;
import java.awt.*;
import java.lang.Math;
public class CanvasObject extends Canvas {
//we create our own version of the Canvas class to
//meet our specific needs.
Image im; //We need an Image and Graphics
Graphics buffer; //buffer in memory for
//double buffering
Color thecolor; //We need to store the current
//color in memory
public void initCan(){ //this routine handles the
//initial setup, including
//storing space in memory
//for the graphics buffer
im=createImage(getSize().width,getSize().height);
buffer=im.getGraphics();
thecolor = Color.black;
}
public void update (Graphics g) {
//We override update to prevent the applet
//from the default action of clearing
//the screen every redraw, and causing screen
//flicker
paint(g);
}
public void prepaint() {
//prepaint draws to the graphics buffer, not
//the screen, so that we do not see drawing
//in progress.
buffer.setColor(thecolor);
buffer.fillRect(0,0,getSize().width,getSize().height);
repaint();
}
public void paint( Graphics g ) {
//paint places the image stored in memory
//onto the screen as a single maneuver.
g.drawImage(im, 0, 0, this);
}
public void changeColor(Color color){
//changecolor allows the main applet to
//change the canvases color.
thecolor = color;
}
}
This code creates a custom designed Canvas object, with our own instructions, but our original code needs to be told to use this CanvasObject instead of a Canvas.
Make the following changes to you applet.
import java.awt.*;
import java.applet.*;
public class ColorChange extends Applet{
//...
// Canvas leftside; //Canvas for drawing
//becomes
CanvasObject leftside;
//...
// leftside = new Canvas(); //Canvas instantiated
//becomes
leftside = new CanvasObject();
//...
//...
//at end of init() routine, add
leftside.initCan(); //initialize graphics buffer
leftside.prepaint(); //do initial screen draw
Custom classes
The benefit of using a predesigned class is that most of the work has been done for you, and the rules for using the classes are well documented. However, sometimes the classes provided in any API do not meet your specific needs. Drawing spaces are a prime example of this. The standard method of creating your own drawing space is to take advantage of class inheritance, and write your own class which inherits for the standard drawing space.
Double Buffering
In our first example, we overrode the Applet classes paint routine. Canvas also has a paint routine, and we will override it here. However, in order to have more fluid graphics, we will introduce what is known as double buffering. When you draw directly to the screen, animation can be very jumpy, as the user sees not only the drawing process as is takes place, but also the process of clearing the screen in between each call to paint. Double buffering refers to the process of creatin a graphics context in memory, drawing to that, and placing the entire image onto the screen at once, without clearing the screen. The key routines in Canvas which need to be overridden are update() and paint(). The update routine by default clears the screen and then calls paint, we just want it to call paint.
Notice that all that the paint routine does is copy the memory image to the screen, we need a second routine, prepaint, to draw to the memory graphics context. Also, the computer needs to know how large to make the memory graphics context, so we need a routine which is called after the custom canvas is sized and placed on the applet to initialize the memory graphics context.
OK, so once that is done, compile it and see what you have.
You should now see the drawing screen as a black screen. The template drawing canvas presented here implements what is known as double buffering, that is, rather than drawing directly to the screen, you draw instead to memory, and place that memory on the screen all at once. This allows you to have flicker free animation, because the user never sees the screen get cleared in between two drawings.
Handling Events
All we need to do now is add in event handling. Event handling in Java 1.1 is handled
by means of "listeners". Basically, you have an object which can say something (post an
event). Other objects listen for those events. The process gernally boils down to
Telling the compiler you are going to listen for events
Telling the object you are listening to that you should be added to its
list of people to tell things to
Adding a routine to do something when told something
The first step involves importing the java.awt.event heirarchy
import java.awt.event.*;
and telling the applet that you are an ActionListener.
public class ColorChange extends Applet implements ActionListener {
The second step involves telling the buttons that you are going to listen
for their action events, so that they will send them to you.
rightside.add(red = new Button("Red")); //add buttons
rightside.add(blue = new Button("Blue")); //to panel
rightside.add(green = new Button("Green"));
// add action listeners
red.addActionListener(this);
blue.addActionListener(this);
green.addActionListener(this);
leftside.initCan();
leftside.prepaint();
Finally, we need an actionPerformed routine that lets the code know what
to do when it gets an event.
public void actionPerformed(ActionEvent e) {
if (e.getSource()==red) {
leftside.changeColor(Color.red);
leftside.prepaint();
} else if (e.getSource()==blue) {
leftside.changeColor(Color.blue);
leftside.prepaint();
} else if (e.getSource()==green) {
leftside.changeColor(Color.green);
leftside.prepaint();
}
}
actionPerformed
There are many different types of events. Buttons post action events, Choice controls
post item events, mouse movements will produce mouse motion events, mouse clicks will
produce mouse events, and on and on. As you go on in your applet programming life,
you will get to know and love each of these unique types of events, but a complete
discussion is beyond the scope of this tutorial.
Compile and view. You should now have your first java applet with double buffered graphics, AWT controls, and event handling!