Mixing Components and Graphics

The Canvas class

As you have seen in this section there are many components that we can use. Previously in the section called “A Simple Applet Example - Hello World!” we used the Applet paint() to draw text on the screen. We also used this method to draw lines, set colours etc. If want to combine components with drawing graphics you could just add a paint() method to the applet and draw. This can however have unpredictable results, where a component could be drawn over a line, or vice-versa.

Java provides us with the Canvas class that acts like any other component, but also allows you to draw graphics directly to it - just like an artist's canvas. The other advantage of using a component is that you can take advantage of the layout managers to control how the applet/application will place the component as it is moved or resized. The Canvas component specifies its co-ordinate system at (0,0) for the top left-hand corner.

Canvas components can handle mouse events like the Applet class. You must subclass Canvas to add the behaviour you require, modifing the paint() method. So, for example:

 3   import java.awt.*;
 4   import java.applet.*;
 6   public class CanvasApplet extends Applet
 7   {
 8     MyCanvas c = new MyCanvas();
10     public void init()
11     {
12       this.add(this.c);
13 	}
14   }
17   class MyCanvas extends Canvas
18   {
19 	public void paint(Graphics g)
20 	{
21 	  g.drawString("Test", 10, 10);
22     }
23   }

This provides your own Canvas class and you can create as many objects of this as you require - each one only displaying "Test" at the (x,y) location (10,10). In this example above I have added the MyCanvas class to the same .java file as CanvasApplet called CanvasApplet.java. This is fine as it is not a public class and so cannot contain a valid main() method. I have not written a constructor in the MyCanvas as it contains the MyCanvas() one (with no parameters) by default and it is sufficient for this applet.

A Functional Canvas Example

Here is an example of an applet that uses the Canvas class. It combines a BorderLayout with a Button component and a modified Canvas component. The applet generates 200 random circles with different colours every time the "randomize!" button is pressed. Figure 9.20, “The Canvas Applet Example” displays the applet running, and you can also run it yourself at CanvasApplet.html.

Figure 9.20. The Canvas Applet Example

The Canvas Applet Example

The source code is as below and also in CanvasApplet.java.

 3   //The Canvas Applet that generates random colour circles
 5   import java.awt.*;
 6   import java.awt.event.*;
 7   import java.applet.*;
10   public class CanvasApplet extends Applet implements ActionListener
11   {
12     MyCanvas c;
14     public void init()
15     {
16 	Button b = new Button("Randomize!");
17 	b.addActionListener(this);
19 	c = new MyCanvas();
21 	this.setLayout(new BorderLayout());
22 	this.add("North", b);
23 	this.add("Center",this.c);
24     }
26     public void actionPerformed(ActionEvent e)
27     {
28 	c.drawCircles();
29     }
31   }
34   class MyCanvas extends Canvas
35   {
36     Color tempColor;
38     public void drawCircles()
39     {
40 	  this.repaint();
41     }
43     public void paint(Graphics g)
44     {
45       for(int i=0; i<200; i++)
46       {
47         tempColor = new Color((float)Math.random(),
48                               (float)Math.random(),
49                               (float)Math.random());
50         g.setColor(tempColor);
51         g.drawOval((int)(Math.random()*200), 
52                    (int)(Math.random()*200), 20, 20);
53       }
54     }
55   }

In this piece of code I have written two classes CanvasApplet and MyCanvas. In the CanvasApplet class an object is created of the MyCanvas class and this is added to the "Center" of the BorderLayout and added the Button object to the "North". When the button "Randomize!" is pressed the drawCircles() method of our MyCanvas is called.

The MyCanvas class extends Canvas and overwrites the paint() method. The drawCircles() just calls this.repaint() which calls the paint() method indirectly, without requiring a Graphics object. To create the Color object we used the constructor:

	Color(float red, float green, float blue);

Where each float has the value 0.0 to 1.0 representing the red, green and blue level. In this case we used Math.random() which generates a double with a value between 0.0 and 1.0. Since it is a double it needs to be converted to a float using the cast conversion. In the drawOval() method the (x,y) position is given by a random number between 0 and 200, by using the Math.random() method and multiplying the value by 200 and then cast converting the double value to an int.