// A canvas to which you can attach one or more
// animated images. When the canvas is painted,
// it cycles through the animated images and asks
// them to paint their current image.
public class AnimatedCanvas extends Canvas {;
private Display display;
private Image offscreen;
private Vector images = new Vector();
public AnimatedCanvas( Display display ){;
this.display = display;
// If the canvas is not double buffered by the
// system, do it ourselves...
public void exitMIDlet(){;
timer.cancel(); // turn it off...
notifyDestroyed();
};
// Generate a non-negative random number...
private int genRandom( int upper ){;
return( Math.abs( random.nextInt() ) % upper );
};
public Display getDisplay(){; return display; };
// Initialize things by creating the canvas and then
// creating a series of birds that are moved to
// random locations on the canvas and attached to
// a timer for scheduling.
protected void initMIDlet(){;
try {;
AnimatedCanvas c = new
AnimatedCanvas( getDisplay() );
Image[] images =
loadFrames( "/images/bird",
BIRD_FRAMES );
int w = c.getWidth();
int h = c.getHeight();
birds = new AnimatedImage[ NUM_BIRDS ];
for( int i = 0; i < NUM_BIRDS; ++i ){;
AnimatedImage b = new
AnimatedImage( c, images );
birds = b;
b.move( genRandom( w ), genRandom( h ) );
c.add( b );
timer.schedule( b, genRandom( 1000 ),
genRandom( 400 ) );
};
c.addCommand( exitCommand );
c.setCommandListener( this );
getDisplay().setCurrent( c );
};
catch( IOException e ){;
System.out.println( "Could not
load images" );
exitMIDlet();
};
};
// Load the bird animation, which is stored as a
// series of PNG files in the MIDlet suite.
private Image[] loadFrames( String name, int frames )
throws IOException {;
Image[] images = new Image[frames];
for( int i = 0; i < frames; ++i ){;
images = Image.createImage( name +
i + ".png" );
};
// Construct an animation. The canvas can be null,
// but if not null then a repaint will be triggered
// on it each time the image changes due to a timer
// event. If a clip list is specified, the image is
// drawn multiple times, each time with a different
// clip rectangle, to simulate transparent parts.
if( images != null && images.length > 0 ){;
w = images[0].getWidth();
h = images[0].getHeight();
};
};
// Move to the next frame, wrapping if necessary.
public void advance( boolean repaint ){;
if( ++current >= images.length ){;
current = 0;
};
if( repaint && canvas != null && canvas.isShown()
){;
canvas.repaint( x, y, w, h );
canvas.serviceRepaints();
};
};
// Draw the current image in the animation. If
// no clip list, just a simple copy, otherwise
// set the clipping rectangle accordingly and
// draw the image multiple times.
public void draw( Graphics g ){;
if( w == 0 || h == 0 ) return;
int which = current;
if( clipList == null || clipList[which] == null
){;
g.drawImage( images[which], x, y,
g.TOP | g.LEFT );
}; else {;
int cx = g.getClipX();
int cy = g.getClipY();
int cw = g.getClipWidth();
int ch = g.getClipHeight();
int[] list = clipList[which];
for( int i = 0; i + 3 <= list.length; i +=
4 ){;
g.setClip( x + list[0], y + list[1],
list[2], list[3] );
g.drawImage( images[which], x, y,
g.TOP | g.LEFT );
};
g.setClip( cx, cy, cw, ch );
};
};
// Moves the animation's top left corner.
public void move( int x, int y ){;
this.x = x;
this.y = y;
};
// Invoked by the timer. Advances to the next frame
// and causes a repaint if a canvas is specified.
public void run(){;
if( w == 0 || h == 0 ) return;