Use the Graphics
drawImage( )
method in your paint routine. Image
objects represent bitmaps. They are normally loaded from a file via
getImage( )
, but can also be synthesized using
createImage( )
. You can’t construct them
yourself, however: the Image
class is abstract.
Once you have an image, displaying it is trivial:
// File graphics/DrawImageDemo.java public void paint(Graphics g) { g.drawImage(0, 0, myImage, this); }
You can get an image by using a routine named, naturally,
getImage( )
. If
your code will be used only in an applet, you can use the
Applet
method getImage( )
, but
if you want it to run in an application as well, you need to use the
Toolkit version. This form takes either a filename or a URL. The
filename, of course, when it turns up in an applet, will fail with a
security exception unless the user installs a policy file. Program
GetImage
shows the code for doing this
both ways:
/* * For Applet, invoke as: <APPLET CODE="GetImage" WIDTH="100" HEIGHT="100"> </APPLET> * For Application, just run it (has own main). */ import java.awt.*; import java.net.*; // for URL class /** This program, which can be an Applet or an Application, * shows a form of Toolkit.getImage( ) which works the same * in either Applet or Application! */ public class GetImage extends java.applet.Applet { Image image; public void init( ) { loadImage( ); } public void loadImage( ) { // Applet-only version: // Image = getImage(getCodeBase( ), "Duke.gif"); // Portable version: getClass().getResource( ) works in either // applet or application, 1.1 or 1.3, returns URL for file name. URL url = getClass( ).getResource("Duke.gif"); image = getToolkit( ).getImage(url); // Shorter portable version: same but avoids temporary variables // image = getToolkit().getImage(getClass( ).getResource("Duke.gif")); } public void paint(Graphics g) { g.drawImage(image, 20, 20, this); } public static void main(String args[]) { Frame f = new Frame("GetImage"); f.addWindowListener(new WindowCloser(f, true)); GetImage myApplet = new GetImage( ); f.add(myApplet); myApplet.init( ); f.setSize(100, 100); f.setVisible(true); myApplet.start( ); } }
You may sometimes want to display an image more than once in the same
panel. Example 12-2 is a program that paints its
background with the same image over and over. We use the
image’s getWidth( )
and getHeight( )
methods to find the image’s size, and the more regular
getSize( )
method on the
component itself. As usual, we
don’t hardcode the window size in the paint( )
method, since the user has the option
of resizing
with the mouse.
Example 12-2. TiledImageComponent.java
import com.darwinsys.util.WindowCloser; import java.awt.*; import java.awt.event.*; import java.net.*; /** * Demo of Tiled Image */ public class TiledImageComponent extends Container { TextField nameTF, passTF, domainTF; Image im; String IMAGE_NAME = "background.gif"; /** Set things up nicely. */ public TiledImageComponent( ) { Label l; setLayout(new FlowLayout( )); add(l = new Label("Name:", Label.CENTER)); add(nameTF=new TextField(10)); add(l = new Label("Password:", Label.CENTER)); add(passTF=new TextField(10)); passTF.setEchoChar('*'), add(l = new Label("Domain:", Label.CENTER)); add(domainTF=new TextField(10)); im = getToolkit( ).getImage(IMAGE_NAME); } /** paint( ) - just tile the background. */ public void paint(Graphics g) { // System.out.println("In paint( )"); if (im == null) return; int iw = im.getWidth(this), ih=im.getHeight(this); if (iw < 0 || ih < 0) // image not ready return; // live to try again later. int w = getSize().width, h = getSize( ).height; // System.out.println(iw + "," + ih + "; " + w + ", " + h); for (int i = 0; i<w+iw; i+=iw) { for (int j = 0; j<h+ih; j+=ih) { // System.out.println("drawImage(im,"+i+","+j+")"); g.drawImage(im, i, j, this); } } } public static void main(String[] av) { Frame f = new Frame("TiledImageComponent Demo"); f.add(new TiledImageComponent( )); f.setSize(200, 200); f.setVisible(true); f.addWindowListener(new WindowCloser(f, true)); } }
In the paint( )
method, we must check that the image not only is not null, but has a
non-negative width and height -- we are more careful than we were
in the previous, somewhat cavalier example. The image will be null
only if something went very wrong in the constructor, but it can have
a negative size. How? In certain creation myths, time ran backward
before the beginning of time; therefore, before an image is fully
created, its size is backwards, that is, it has a width and height of
-1. The getImage( )
method doesn’t actually get the image, you see. It creates the
Image
object, true, but it doesn’t
necessarily load all the bits: it starts a background thread to do
the reading, and returns. This dates from the days when the Web was
slower and took a long time to fully load an image. In particular,
there might be some image file formats (some kinds of TIFF files,
perhaps) where you don’t know the actual image size until
you’ve read the entire file. Thus, when getImage( )
returns, the Image
object is created,
but its size is set to -1, -1. Since there are now two threads
running (see Chapter 24), there are two possible
outcomes. Either the image-reading thread reads enough to know the
width and height before you need them, or you need them before the
thread reads enough to know them. The curious-looking code in
paint( )
is defensive about this. You should be
too.
But what if you really need the size of the image, for example to lay
out a larger panel? If you read a bit of the Image
documentation, you might think you can use the prepareImage( )
method to ensure that the object has been loaded. Unfortunately, this
method can get you stuck in a loop if the image file is missing,
because prepareImage
will never return true! If
you need to be sure, you must construct a
MediaTracker
object to ensure that the image has been
loaded successfully. That looks something like this:
/** * This CODE FRAGMENT shows using a MediaTracker to ensure * that an Image has been loaded successfully, then obtaining * its Width and Height. The MediaTracker can track an arbitrary * number of Images; the "0" is an arbitrary number used to track * this particular image. */ Image im; int imWidth, imHeight; public void setImage(Image i) { im = i; MediaTracker mt = new MediaTracker(this); // use of "this" assumes we're in a Component subclass. mt.addImage(im, 0); try { mt.waitForID(0); } catch(InterruptedException e) { throw new IllegalArgumentException( "InterruptedException while loading Image"); } if (mt.isErrorID(0)) { throw new IllegalArgumentException( "Couldn't load image"); } imWidth = im.getWidth(this); imHeight = im.getHeight(this); }
You can ask the
MediaTracker
for its status at any time using
the method status(int
ID,
boolean
load)
, which returns an
integer made by or
-ing together the values shown
in Table 12-1. The boolean load
flag, if true, tells the MediaTracker
to start
loading any images that haven’t yet been started. A related
method, statusAll( )
, returns the inclusive
or
of any flags applying to images that have
started loading.
Table 12-1. MediaTracker status values
Flag |
Meaning |
---|---|
ABORTED |
Downloading of at least one item was aborted. |
COMPLETE |
Downloading of all items completed without error. |
ERRORED |
Something went wrong while downloading at least one item. |
LOADING |
Downloading is ongoing. |
You can shorten the previous code by using the
Swing ImageIcon
class,
which includes this functionality. The ImageIcon
class has several constructor forms, one of which takes just a
filename argument. ImageIcon
uses a
MediaTracker
internally; you can ask for its
status using the
ImageIcon
’s
getImageLoadStatus( )
method, which
returns the
same
values as
MediaTracker
’s statusAll( )/statusID( ).
3.145.17.18