|         在SUN的技术文章里面有一篇文章讲述了如何基于MIDP1.0实现动画,个人觉得很不错。在MIDP1.0中并为直接对动画提供支持,如果我们明白动画的原理,熟悉Timer和TimerTask的话实现动画并非很困难的事情。
        动画实际上就是一系列连续的帧,当他们变换的足够快的时候,我们人的眼睛就会觉得他是运动的。所以如果我们能够周期性的切换画面,从第一帧到第二帧到第三帧 ......那么我们就可以制作出动画的效果了。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);         }     } } 
  
 
  |