It is sometimes said, only half in
jest, that WWW stands for “World Wide Wait”, primarily
because of the amount of time it takes for graphics-heavy pages to
load. Although reading large files off a hard drive can be
time-consuming, most users have trained themselves not to notice the
time it takes; file loading seems to happen instantaneously. However,
this is not the case when files are loaded from a network,
particularly when that network is the Internet. It is not uncommon
for even small images to take several minutes to load. Since Java
loads images in a different thread than the main execution of a
program, a program can do something while the pictures are
downloaded. However, programs don’t get impatient; users do. It
is a good idea to keeps users informed about how much of an image has
been loaded and how much longer they can expect to wait. The
java.awt.image.ImageObserver
interface allows you
to monitor the loading process so that you can keep the user informed
and use the image as quickly as possible once it does load.
When discussing getImage( )
, I said that the
method returned immediately, before downloading the image.
Downloading begins when you try to display the image or do something
else that forces loading to start (for example, passing the
Image
object to the prepareImage( )
method of java.awt.Component
). Because
loading takes place in a separate thread, programs don’t have
to spin their wheels while lots of pictures are downloaded; they can
continue doing something useful, even if that’s limited to
keeping the user informed. However, loading an image asynchronously
creates a new problem: how do you know when it’s ready? An
Image
object exists as soon as getImage( )
returns, long before anything is known about the image it
represents. You can call an Image
’s methods
before it has finished loading, but the results are rarely what you
want or expect. For example, the getWidth( )
and
getHeight( )
methods return -1 until enough data
has been loaded for these values to be
known.
The ImageObserver
interface allows an object to
monitor the progress of loading and to take action (such as drawing
the Image
) when the Image
is
ready for use. You can also use ImageObserver
objects to track the progress of an image that is being created from
scratch, using java.awt.image.MemoryImageSource
or
some other instance of the
java.awt.image.ImageProducer
interface. This
interface consists of a group of constants and a single method,
imageUpdate( )
:
public boolean imageUpdate(Image image, int infoflags, int x, int y, int width, int height)
java.awt.Component
implements
ImageObserver
, so all its subclasses (including
java.applet.Applet
,
java.awt.Canvas
,
java.awt.Panel
,
javax.swing.JButton
, and many more) do as well.
If you’ve done much programming in Java, you’ve probably
used the ImageObserver
class without thinking
about it: the this
that you stick at the end of a
call to drawImage( )
says to use the current
component as an ImageObserver
:
g.drawImage(theImage, 0, 0, this);
If the image is complete, then it’s drawn and that’s
that. On the other hand, if the image is not ready, then the
component’s imageUpdate( )
method will be called
periodically, giving it the chance to check the status of the image
and respond accordingly. Other methods that take
ImageObserver
objects as arguments include the
getWidth( )
, getHeight( )
, and getProperty( )
methods of
java.awt.Image
; the checkImage( )
and prepareImage( )
methods of
java.awt.Component
; and the
setImageObserver( )
method of
javax.swing.ImageIcon
. Passing an
ImageObserver
to any of these methods signals that
the object is interested in the image and should be notified through
the imageUpdate( )
method when the image’s
status changes. Variables that are set using a method such as
getWidth( )
or getHeight( )
,
and pictures that are drawn on the screen using drawImage( )
, are not updated automatically when imageUpdate( )
is called. It’s your job to update them when the
image changes.
If you want to create your own ImageObserver
, you
first create a subclass of any Component
or create
your own class that implements the ImageObserver
interface. Be sure to import
java.awt.image.ImageObserver
or
java.awt.image.*
Importing
java.awt.*
does not automatically import
java.awt.image.ImageObserver
. Your new class, or
your Component
subclass, must include an
imageUpdate( )
method. As you’ll see in the
next section, there are a series of tests that imageUpdate( )
can perform to determine the current status of the image.
Example 9.6 is an applet that loads an image from
the Net. It supplies its own imageUpdate( )
method
(overriding the imageUpdate( )
method
Applet
inherits from Component
)
to see whether the image has loaded. Until the image has finished
loading, the applet displays the words “Loading Picture. Please
hang on”. Once the image has fully loaded, the applet displays
it.
Example 9-6. Load an Image
import java.awt.*; import java.applet.*; import java.awt.image.*; public class DelayedImageView extends Applet { private Image picture; public void init( ) { this.picture = this.getImage(this.getDocumentBase( ), "cup.gif"); } public void paint(Graphics g) { if(!g.drawImage(this.picture, 0, 0, this)) { g.drawString("Loading Picture. Please hang on", 25, 50); } } public boolean imageUpdate(Image image, int infoflags, int x, int y, int width, int height) { if ((infoflags & ImageObserver.ALLBITS) == ImageObserver.ALLBITS) { this.repaint( ); return false; } else { return true; } } }
There are a couple of things to note about this example. First, the
drawImage( )
method returns a
boolean
. Many programmers don’t realize
this, since the return value of drawImage( )
is
often ignored. However, that boolean
tells you
whether the image was drawn successfully. If it was,
drawImage( )
returns true
.
Otherwise, drawImage( )
returns
false
.
Second, the imageUpdate( )
method is called by the
ImageObserver
when necessary; you do not call it
explicitly. It returns false
if the image is
complete and no longer needs to be updated. It returns
true
if the image still needs to be updated.
Let’s repeat that point, since it’s easy to get backwards. The
imageUpdate( )
method should return
false
if the image is complete and
true
if the image is not complete. The
imageUpdate( )
method answers the question “Does
this image need to be updated?” It does not answer the question “Is
this image complete?”
Now, let’s look at what imageUpdate( )
does.
The Image
being loaded is passed into
imageUpdate( )
through the
image
argument. Various mnemonic constants are
combined to form the infoflags
argument, which
indicates what information about the image is now available. For
instance, ImageObserver.ALLBITS
is 32, and means
that the Image
is complete. You test whether the
ALLBITS
flag is set in
infoflags
by logically and
ing it with the mnemonic constant
ImageObserver.ALLBITS
and then seeing if the
result is equal to ImageObserver.ALLBITS
. This
chunk of Boolean algebra looks complicated but is more efficient than
the alternatives.
The precise meaning of the x
,
y
, width
, and
height
arguments depends on the contents of the
infoflags
argument. The flags used in this
argument are described in the next section.
The imageUpdate( )
method needs to do three
things:
The ImageObserver
interface defines eight mnemonic
constants, which are used as flags to report the image’s status
in calls to imageUpdate( )
. Table 9.1 lists the constants and their meanings.
Table 9-1. ImageObserver Constants
Flag |
Meaning If the Flag Is Set |
---|---|
|
The width of the image is available in the |
|
The height of the image is available in the |
|
The properties of the image are now available and can be accessed
with the |
|
Some portion of the data needed to draw the image has been delivered.
The bounding box of the available pixels is given by the
|
|
Another complete frame of a multiframe image is now available. The
|
|
The image is now complete. The |
|
The image encountered an error while loading. No further information
will be forthcoming, and attempts to draw the image will fail. The
|
|
Loading aborted before the image was complete. No more information
will become available without further action to reload the
|
For example, to test whether the width and the height of the image
are known, you would put the following code in the
imageUpdate( )
method:
if ((infoflags & ImageObserver.WIDTH) == ImageObserver.WIDTH && (infoflags & ImageObserver.HEIGHT) == ImageObserver.HEIGHT) {...}
The difference between the ImageObserver.ERROR
and
ImageObserver.ABORT
flags can be confusing. You
would get an ImageObserver.ERROR
if the image data
was incorrect—for example, you tried to download a mangled
file. If you try to load the image again, you’ll get the same
bad data. ImageObserver.ABORT
usually indicates
some kind of network error, such as a timeout; if you try to load the
image again, you might succeed.
18.220.64.128