動畫實際上就是一系列連續的幀,當他們變換的足夠快的時候,我們人的眼睛就會覺得他是運動的。所以如果我們能夠周期性的切換畫面,從第一幀到第二幀到第三幀 ......那麼我們就可以制作出動畫的效果了。Timer和TimerTask正好可以很好的幫我們完成這一功能,如果你還不熟悉這兩個類可以參考使用Java中的Timer和TimerTask。我們首先擴展TimerTask實現AnimatedImage類,它的功能就是在特定位置畫圖
import Java.util.*;
import Javax.microedition.lcdui.*;
// Defines an animated image, which is just a set
// of images of equal size which are drawn in turn
// to simulate movement.
public class AnimatedImage extends TimerTask
{
private Canvas canvas;
private Image[] images;
private int[][] clipList;
private int current;
private int x;
private int y;
private int w;
private int h;
// Construct an animation with no canvas.
public AnimatedImage(Image[] images)
{
this(null, images, null);
}
// Construct an animation with a null clip list.
public AnimatedImage(Canvas canvas, Image[] images)
{
this(canvas, images, null);
}
// 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.
public AnimatedImage(Canvas canvas, Image[] images, int[][] clipList)
{
this.canvas = canvas;
this.images = images;
this.clipList = clipList;
if (images != null && clipList != null)
{
if (clipList.length < images.length)
{
throw new IllegalArgumentException();
}
}
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;
advance(true);
}
}
我們需要預先在jar文件裡面存儲幾個大小一樣,形態有序的圖片,在MIDlet啟動的時候把它裝載在一個Image數組,並把它作為參數傳遞給AnimatedImage。
private Image[] loadFrames( String name, int frames )
throws IOException {
Image[] images = new Image[frames];
for( int i = 0; i < frames; ++i ){
images[i] = Image.createImage( name + i +
".png" );
}
return images;
}
Image[] frames = loadFrames( "/images/bird", 7 );
AnimatedImage ai = new AnimatedImage( frames );
ai.move( 20, 20 ); // set top-left to 20,20
下面是兩個測試效果的AnimatedCanvas和AnimationTest類的源代碼,在這裡你可以得到全部的源文件和圖片資源。仔細讀讀這幾個類是很有好處的。有一點值得一提的是,在繪制圖片的時候應該判斷設備是否支持雙緩沖,如果不支持要使用雙緩沖技術避免畫面閃爍。而且我們也不適宜把動畫圖片作的過大,這樣會增大jar文件的大小,同時會給設備造成很大的負擔。
protected void paint(Graphics g)
{
Graphics saved = g;
if (offscreen != null)
{
g = offscreen.getGraphics();
}
g.setColor(255, 255, 255);
g.fillRect(0, 0, getWidth(), getHeight());
int n = images.size();
for (int i = 0; i < n; ++i)
{
AnimatedImage img = (AnimatedImage) images.elementAt(i);
img.draw(g);
}
if (g != saved)
{
saved.drawImage(offscreen, 0, 0, Graphics.LEFT | Graphics.TOP);
}
}
import Java.io.*;
import Java.util.*;
import Javax.microedition.lcdui.*;
import Javax.microedition.midlet.*;
// MIDlet that displays some simple animations.
// Displays a serIEs of birds on the screen and
// animates them at different (random) rates.
public class AnimationTest extends MIDlet implements CommandListener
{
private static final int BIRD_FRAMES = 7;
private static final int NUM_BIRDS = 5;
private Display display;
private Timer timer = new Timer();
private AnimatedImage[] birds;
private Random random = new Random();
public static final Command exitCommand = new Command("Exit", Command.EXIT,
1);
public AnimationTest()
{
}
public void commandAction(Command c, Displayable d)
{
if (c == exitCommand)
{
exitMIDlet();
}
}
protected void destroyApp(boolean unconditional)
throws MIDletStateChangeException
{
exitMIDlet();
}
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[i] = 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[i] = Image.createImage(name + i + ".png");
}
return images;
}
protected void pauseApp()
{
}
protected void startApp() throws MIDletStateChangeException
{
if (display == null)
{
display = Display.getDisplay(this);
initMIDlet();
}
}
}
import Java.util.*;
import Javax.microedition.lcdui.*;
// 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...
if (!isDoubleBuffered())
{
offscreen = Image.createImage(getWidth(), getHeight());
}
}
// Add an animated image to the list.
public void add(AnimatedImage image)
{
images.addElement(image);
}
// Paint the canvas by erasing the screen and then
// painting each animated image in turn. Double
// buffering is used to reduce flicker.
protected void paint(Graphics g)
{
Graphics saved = g;
if (offscreen != null)
{
g = offscreen.getGraphics();
}
g.setColor(255, 255, 255);
g.fillRect(0, 0, getWidth(), getHeight());
int n = images.size();
for (int i = 0; i < n; ++i)
{
AnimatedImage img = (AnimatedImage) images.elementAt(i);
img.draw(g);
}
if (g != saved)
{
saved.drawImage(offscreen, 0, 0, Graphics.LEFT | Graphics.TOP);
}
}
}