Displaying Images in an Applet
Applets can load an image file and paint it onto their drawing surface. Loading is handled by a method
inherited from Applet - getImage() - and drawing is handled by a method on the
Graphics object you already know - drawImage(). The tricky part isn't the API,
it's understanding that loading happens in the background.
Loading an Image: getImage()
Call getImage() inside init(), passing a base URL and a filename. It returns
immediately with an Image object, even though the actual file may not have finished
downloading yet:
Image img = getImage(getDocumentBase(), "canvas-bg.png");
getImage() is asynchronous: it kicks off loading on a background thread and
returns straight away. You don't get an error if the file is missing yet, and you don't get a fully
decoded image right away either - the Image object simply fills in as data arrives.
getDocumentBase() vs getCodeBase()
getImage() needs a base URL to resolve the filename against. Applet gives you
two choices, and picking the right one matters once your HTML page and your compiled classes live in
different places:
| Method | Resolves relative to | Use it when... |
|---|---|---|
getDocumentBase() | The URL of the HTML page embedding the applet | The resource (image/audio) sits next to the HTML file |
getCodeBase() | The URL of the applet's own .class/JAR file | The resource is bundled alongside the compiled applet code |
For PaintApplet, we'll assume canvas-bg.png sits in the same folder as the HTML
page that embeds it, so getDocumentBase() is the right choice.
Drawing an Image: drawImage()
Once you have an Image, draw it inside paint() with Graphics.drawImage().
The stretched-to-fit overload takes a target width and height:
if (backgroundImage != null) {
g.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this);
}
The last argument, this, is an ImageObserver. Applet (through
Component) already implements that interface, so passing this is enough -
AWT uses it to automatically trigger another repaint() once more image data has arrived,
which is exactly why you don't have to manually wait for the image in simple cases like this one. The
null check matters too: right after init() runs, backgroundImage may not have
any pixels loaded yet, but it's never actually null once getImage() has returned
an object - the check here is just good defensive habit for the field before assignment.
java.awt.MediaTracker
class. You register the image with a tracker and call a blocking method like waitForID(),
which pauses until loading completes or fails. Most simple drawing code, like the example below, doesn't
need this - drawImage()'s own observer callback is enough.
Extending PaintApplet: A Background Image
Add one field, load it in init(), and draw it first in paint() - before the
title, the border, and the shapes - so it acts as a backdrop for everything else already on the canvas.
Only the changed parts of the class are shown; everything else (mouse dragging, keyboard color switching)
stays exactly as it was at the end of Applet Parameters.
// New field
private Image backgroundImage;
public void init() {
// ...existing title/bgcolor/parameter handling stays here...
// New: kick off loading the background image
backgroundImage = getImage(getDocumentBase(), "canvas-bg.png");
// ...existing addMouseMotionListener / addKeyListener stay here...
}
public void paint(Graphics g) {
// New: draw the background image first, stretched to fill the canvas
if (backgroundImage != null) {
g.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this);
}
// ...everything below is unchanged: title text, border, drawShapes(), drawn points...
g.setFont(titleFont);
g.setColor(Color.BLACK);
g.drawString(title, 20, 30);
// ... rest of paint() as before ...
}
Because the image is drawn before anything else in paint(), the title text, the border, the
fixed shapes, and the freehand strokes all render on top of it, exactly as you'd expect for a background
layer.
PaintApplet a sound effect
using the same background-loading idea, applied to AudioClip instead of Image.