Previous | Next | Trail Map | Creating a User Interface | Using Components, the GUI Building Blocks

How to Use Canvases

The Canvas (in the API reference documentation) class exists to be subclassed. It does nothing on its own; it merely provides a way for you to implement a custom Component. For example, Canvases are useful as display areas for images and custom graphics, whether or not you wish to handle events that occur within the display area.

Canvases are also useful when you want a control -- a button, for example -- that doesn't look like the the default implementation of the control. Since you can't change a standard control's appearance by subclassing its corresponding Component (Button, for example), you must instead implement a Canvas subclass to have both the look you want and the same behavior as the default implementation of the control.

When implementing a Canvas subclass, take care to implement the minimumSize() and preferredSize() methods to properly reflect your canvas's size. Otherwise, depending on the layout your canvas's container uses, your canvas could end up too small -- perhaps even invisible.

Here is an applet that uses two instances of a Canvas subclass: ImageCanvas.


You can't run 1.0 Java applets. Here's what you'd see if you could:


Note: Because some old browsers don't support 1.1, the above applet is a 1.0 version (here is the 1.0 code; here's the 1.1 code). To run the 1.1 version of the applet, go to example-1dot1/ImageApplet.html.

Below is the code for ImageCanvas. (You can find it in electronic form in the file ImageApplet.java.) Because image data is downloaded asynchronously, an ImageCanvas doesn't know how big it should be until some time after it's created. For this reason, ImageCanvas uses the initial width and height suggested by its creator until the image size data is available. When the image size data becomes available, the ImageCanvas changes the size its preferredSize() and minimumSize() methods return, attempts to resize itself, and then requests that its top-level container's layout be adjusted accordingly and redisplayed.

class ImageCanvas extends Canvas {
    Container pappy;
    Image image;
    boolean trueSizeKnown = false;
    Dimension minSize;
    int w, h;

    public ImageCanvas(Image image, Container parent, 
		              int initialWidth, int initialHeight) {
	if (image == null) {
	    System.err.println("Canvas got invalid image object!");
	    return;
	}

	this.image = image;
        pappy = parent;

	w = initialWidth;
	h = initialHeight;

	minSize = new Dimension(w,h);
    }

    public Dimension preferredSize() {
 	return minimumSize();
    }

    public synchronized Dimension minimumSize() {
	return minSize;
    }

    public void paint (Graphics g) {
	if (image != null) {
	    if (!trueSizeKnown) {
	        int imageWidth = image.getWidth(this);
	        int imageHeight = image.getHeight(this);

	        if ((imageWidth > 0) && (imageHeight > 0)) {
		    trueSizeKnown = true;

		    //Ooh... component-initiated resizing.
		    w = imageWidth;
		    h = imageHeight;
		    minSize = new Dimension(w,h);
		    resize(w, h);
		    pappy.layout();
		    pappy.repaint();
	        }
	    }

	    g.drawRect(0, 0, w - 1, h - 1);
	    g.drawImage(image, 0, 0, this);
	}
    }
}
For more information on drawing graphics, see the Working with Graphics(in the Creating a User Interface trail) lesson. For an example of implementing a Canvas that both draws custom graphics and handles events, see the RectangleDemo applet code. You can see RectangleDemo in action in Drawing Shapes(in the Creating a User Interface trail).


Previous | Next | Trail Map | Creating a User Interface | Using Components, the GUI Building Blocks