Java/J2ME/Animation

Материал из Java эксперт
Перейти к: навигация, поиск

A MIDlet that displays the Doggy animation.

/*
 * Copyright (c) 2000-2001 Sun Microsystems, Inc. All Rights Reserved.
 */
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
/**
 * A MIDlet that displays the Doggy animation.
 *
 * @author Mark A. Patel - Motorola, Inc.
 * @author Roger Riggs - Sun Microsystems, Inc.
 **/
public class DoggyMIDlet extends MIDlet implements CommandListener {
    Command cmdExit;
    /**
     * Constructs a new DoggyMIDlet
     **/
    public DoggyMIDlet() {
        cmdExit = new Command("Exit", Command.EXIT, 1);
    }
    
    /**
     * Starts the app by creating a new Doggy instance and displaying it
     **/
    protected void startApp() throws MIDletStateChangeException {
        Doggy d;
        d = new Doggy();
        d.addCommand(cmdExit);
        d.setCommandListener(this);
        
        Display.getDisplay(this).setCurrent(d);
  
    }
    
    protected void pauseApp() {
    }
    
    protected void destroyApp(boolean unconditional)
  throws MIDletStateChangeException {
    }
    
    public void commandAction(Command c, Displayable d) {
        if (c == cmdExit) {
            try {
                destroyApp(false);
            } catch (Exception e) {}
            notifyDestroyed();
        }
    }
}
 class Doggy extends Canvas implements Runnable {
  /**
   * Number of frames in the animation
   **/
  static final int FRAME_COUNT = 17;
  /**
   * Normal frame delay (milliseconds)
   **/
  static final int FRAME_DELAY = 180;
  /**
   * Frame delay for the last frame where the dog is sleeping
   **/
  static final int LAST_FRAME_DELAY = 3000;
  /**
   * Relative horizontal position where each of the frames 
   * should be rendered. 0 represents the left edge of the screen
   * and 1024 represents the right edge of the run distance 
   * (1024 is used so that scaling can be performed using
   * simple bit shifts instead of division operations). 
   **/
  static final int[] framePositions = {
    0, 50, 186, 372, 558, 744, 930, 1024, 1024,
    834, 651, 465, 279, 93, 0, 0, 0
  };
  /**
   * An Image containing the 17 frames of the dog running,
   * stacked vertically.
   * Using a single image is much more efficient than using several 
   * images with each containing a single frame.
   * Each frame can be rendered seperately by setting the clip 
   * region to the size of a single frame and then
   * rendering the image at the correct position so that the desired 
   * frame isaligned with the clip region.
   **/
  Image doggyImages = null;
  /**
   * Width of a single animation frame
   **/
  int frameWidth = 0;
  /**
   * Height of a single animation frame
   **/
  int frameHeight = 0;
  /**
   * Index of the current frame
   **/
  int frameIndex = 0;
  /**
   * The distance, in pixels, that the dog can run (screen width less
   * the width of a single frame)
   **/
  int runLength = 0;
  /**
   * Indicates if the animation is currently running
   **/
  boolean running = false;
  /**
   * Called when this Canvas is shown.  This method starts the timer 
   * that runs the animation sequence.
   **/
  protected void showNotify() {
    if (doggyImages == null) {
      try {
        doggyImages =
          Image.createImage("/examples/animation/Doggy.png");
        frameWidth = doggyImages.getWidth();
        frameHeight = doggyImages.getHeight() / FRAME_COUNT;
      } catch (Exception ioe) {
        return; // no image to animate
      }
    }
    runLength = getWidth() - frameWidth;
    running = true;
    frameIndex = 0;
    new Thread(this).start();
  }
  /**
   * Called when this Canvas is hidden.  This method stops the
   * animation timer to free up processing
   * power while this Canvas is not showing.
   **/
  protected void hideNotify() {
    running = false;
  }
  public void run() {
    // Need to catch InterruptedExceptions and bail if one occurs
    try {
      while (running) {
        Thread.sleep((frameIndex == FRAME_COUNT - 1) ?
               LAST_FRAME_DELAY : FRAME_DELAY);
        // Remember the last frame index so we can compute
        // the repaint region
        int lastFrameIndex = frameIndex;
        // Update the frame index
        frameIndex = (frameIndex + 1) % FRAME_COUNT;
        // Determine the left edge of the repaint region
        int repaintLeft = framePositions[lastFrameIndex];
        int repaintRight = framePositions[frameIndex];
        if (framePositions[lastFrameIndex] > framePositions[frameIndex]) {
          repaintLeft = framePositions[frameIndex];
          repaintRight = framePositions[lastFrameIndex];
        }
        // Scale the repaint coordinates to the width of the screen
        repaintLeft = (repaintLeft * runLength) >> 10;
        repaintRight = (repaintRight * runLength) >> 10;
        // Trigger a repaint of the affected portion of the screen
        // Repaint the region where the last frame was rendered
        // (ensures that it is cleared)
        repaint(repaintLeft, 0,
            frameWidth + repaintRight - repaintLeft, frameHeight);
      }
    } catch (InterruptedException e) {}
  }
  public void paint(Graphics g) {
    // Clear the background (fill with white)     
    // The clip region will limit the area that
    // actually gets cleared to save time
    g.setColor(0xFFFFFF);
    g.fillRect(0, 0, getWidth(), getHeight());
    // Translate the graphics to the appropriate
    // position for the current frame
    g.translate((framePositions[frameIndex] * runLength) >> 10, 0);
    // Constrain the clip region to the size of a single frame
    g.clipRect(0, 0, frameWidth, frameHeight);
    // Draw the current frame by drawing the entire image with
    // the appropriate vertical offset so that the desired frame
    // lines up with the clip region.
    g.drawImage(doggyImages, 0, -(frameIndex * frameHeight),
          Graphics.LEFT + Graphics.TOP);
  }
}





Animated Timer

/*--------------------------------------------------
 * AnimatedTimer - Main midlet.
 * Shows canvas with an animated timer. Includes
 * configuration options to start/stop the timer
 * and to adjust the sleep interval of the thread
 *
 * Example from the book:     Core J2ME Technology
 * Copyright John W. Muchow   http://www.CoreJ2ME.ru
 * You may use/modify for any non-commercial purpose 
 *-------------------------------------------------*/
import java.util.Stack;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.rumand;
import javax.microedition.lcdui.rumandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.List;
import javax.microedition.midlet.MIDlet;
public class AnimatedTimer extends MIDlet {
  private Display display; // The display
  protected TimerCanvas cvTimer; // Canvas to display timer
  protected OptionsList lsOptions; // List to change timer options
  protected SleepForm fmSleep; // Form with gauge to set timer sleep
  protected DisplayManager displayMgr; // Class to help manage screens
  public AnimatedTimer() {
    display = Display.getDisplay(this);
    cvTimer = new TimerCanvas(this);
    lsOptions = new OptionsList("Timer options", List.IMPLICIT, this);
    fmSleep = new SleepForm("Adjust sleep", this);
    // Create a display manager object
    displayMgr = new DisplayManager(display, cvTimer);
  }
  protected void startApp() {
    // Start with the canvas
    display.setCurrent(cvTimer);
  }
  protected void pauseApp() {
  }
  protected void destroyApp(boolean unconditional) {
  }
  public void exitMIDlet() {
    destroyApp(true);
    notifyDestroyed();
  }
}
/*--------------------------------------------------
 * Use a stack to push and pop displayable objects
 *
 * public void pushDisplayable(Displayable)
 * public void popDisplayable()
 * public void home()  
 *
 * Example from the book:     Core J2ME Technology
 * Copyright John W. Muchow   http://www.CoreJ2ME.ru
 * You may use/modify for any non-commercial purpose 
 *-------------------------------------------------*/
class DisplayManager extends Stack {
  private Display display; // Reference to Display object
  private Displayable mainDisplayable; // Main displayable for MIDlet
  private Alert alStackError; // Alert for error conditions
  /*--------------------------------------------------
   * Display manager constructor
   *-------------------------------------------------*/
  public DisplayManager(Display display, Displayable mainDisplayable) {
    // Only one display object per midlet, this is it
    this.display = display;
    this.mainDisplayable = mainDisplayable;
    // Create an alert displayed when an error occurs
    alStackError = new Alert("Displayable Stack Error");
    alStackError.setTimeout(Alert.FOREVER); // Modal
  }
  /*--------------------------------------------------
   * Push the current displayable onto stack and set
   * the passed in displayable as active
   *-------------------------------------------------*/
  public void pushDisplayable(Displayable newDisplayable) {
    push(display.getCurrent());
    display.setCurrent(newDisplayable);
  }
  /*--------------------------------------------------
   * Return to the main displayable object of MIDlet
   *-------------------------------------------------*/
  public void home() {
    while (elementCount > 1)
      pop();
    display.setCurrent(mainDisplayable);
  }
  /*--------------------------------------------------
   * Pop displayable from stack and set as active
   *-------------------------------------------------*/
  public void popDisplayable() {
    // If the stack is not empty, pop next displayable
    if (empty() == false)
      display.setCurrent((Displayable) pop());
    else
      // On error show an alert
      // Once acknowledged, set "mainDisplayable" as active
      display.setCurrent(alStackError, mainDisplayable);
  }
}
/*--------------------------------------------------
 * Class TimerCanvas
 *
 * Animate a sequence of images to simulate 
 * a moving timer
 *
 * Example from the book:     Core J2ME Technology
 * Copyright John W. Muchow   http://www.CoreJ2ME.ru
 * You may use/modify for any non-commercial purpose 
 *-------------------------------------------------*/
class TimerCanvas extends Canvas implements Runnable, CommandListener {
  private AnimatedTimer midlet; // Main midlet
  private Command cmExit; // Exit midlet
  private Command cmOptions; // Display options list
  private Image im = null; // Sequence of images
  private int imageCount = 4; // Four images in the sequence
  private int imageWidth; // Width of one image in the sequence
  private int imageHeight; // Height of one image in the sequence
  private int imageIndex; // Current image in the sequence
  private int translate_x; // Translated x and y
  private int translate_y;
  private int viewport_x; // Location of the viewport
  private int viewport_y;
  private boolean active = false; // Timer active?
  private boolean requestedToStop = false; // Did user request to stop timer
  private int sleepTime = 400; // Current sleep time (milliseconds)
  public TimerCanvas(AnimatedTimer midlet) {
    // Call canvas constructor
    super();
    // Save reference to MIDlet so we can
    // access the display manager class
    this.midlet = midlet;
    // Create commands & listen for events
    cmExit = new Command("Exit", Command.EXIT, 1);
    cmOptions = new Command("Config", Command.SCREEN, 2);
    addCommand(cmExit);
    addCommand(cmOptions);
    setCommandListener(this);
  }
  /*--------------------------------------------------
   * Application manager is about to display canvas
   *-------------------------------------------------*/
  protected void showNotify() {
    if (im == null) {
      try {
        // Read the png from a file and get width and
        // height of one image in the sequence
        im = Image.createImage("/timer.png");
        imageHeight = im.getHeight();
        imageWidth = im.getWidth() / imageCount;
        // Get the coordinates for x/y of viewport
        // Viewport is centered on the display
        viewport_x = (getWidth() / 2) - (imageWidth / 2);
        viewport_y = (getHeight() / 2) - (imageHeight / 2);
        // Set first translated coordinate to match viewport
        translate_x = viewport_x;
        translate_y = viewport_y;
      } catch (Exception e) {
        System.err.println("Unable to read png file.");
      }
      // Begin with the first image in the sequence
      imageIndex = 0;
    }
    // If the user has not requested to stop the timer...
    if (!requestedToStop)
      active = true;
    new Thread(this).start();
  }
  /*--------------------------------------------------
   * Application manager is no longer displaying canvas
   *-------------------------------------------------*/
  protected void hideNotify() {
    active = false;
  }
  /*--------------------------------------------------
   * Draw next timer in sequence
   *-------------------------------------------------*/
  protected void paint(Graphics g) {
    if (im != null) {
      // Due to a bug in MIDP 1.0.3 we need to
      // force a clear of the display
      g.setColor(255, 255, 255); // White pen
      g.fillRect(0, 0, getWidth(), getHeight());
      g.setColor(0, 0, 0); // Black pen
      // Viewport at center of display
      g.setClip(viewport_x, viewport_y, imageWidth, imageHeight);
      // Draw image at translated coordinates
      g.drawImage(im, translate_x, translate_y, Graphics.TOP
          | Graphics.LEFT);
    }
  }
  /*--------------------------------------------------
   * Loop forever, translating image coordinates
   *-------------------------------------------------*/
  public void run() {
    try {
      while (active) {
        Thread.sleep(sleepTime);
        repaint();
        // Reached the last image in sequence
        if (imageIndex == imageCount - 1) {
          // Reset translated coordinates
          translate_x = viewport_x;
          translate_y = viewport_y;
        } else {
          // Translate coordinate system to the left
          translate_x -= imageWidth;
        }
        // Which image in the sequence is next
        imageIndex = (imageIndex + 1) % imageCount;
      }
    } catch (InterruptedException e) {
    }
  }
  /*--------------------------------------------------
   * Called from the "Config" options menu
   *-------------------------------------------------*/
  public void startTimer() {
    requestedToStop = false;
    active = true;
    repaint();
  }
  /*--------------------------------------------------
   * Called from the "Config" options menu
   *-------------------------------------------------*/
  public void stopTimer() {
    requestedToStop = true;
    active = false;
    repaint();
  }
  /*--------------------------------------------------
   * Called from form/gauge to adjust sleep
   *-------------------------------------------------*/
  public void setSleep(int sleepTime) {
    this.sleepTime = sleepTime;
  }
  /*--------------------------------------------------
   * Called from form/gauge to adjust sleep
   *-------------------------------------------------*/
  public int getSleep() {
    return sleepTime;
  }
  /*--------------------------------------------------
   * Command event handling
   *-------------------------------------------------*/
  public void commandAction(Command c, Displayable s) {
    if (c == cmOptions) {
      // Push current displayable and show the options list
      midlet.displayMgr.pushDisplayable(midlet.lsOptions);
    } else if (c == cmExit) {
      midlet.exitMIDlet();
    }
  }
}
/*--------------------------------------------------
 * Class SleepForm
 *
 * Form with gauge to adjust sleep interval of timer
 *
 * Example from the book:     Core J2ME Technology
 * Copyright John W. Muchow   http://www.CoreJ2ME.ru
 * You may use/modify for any non-commercial purpose 
 *-------------------------------------------------*/
class SleepForm extends Form implements CommandListener {
  private AnimatedTimer midlet; // Main midlet
  private Command cmBack, // Back to options list
      cmHome, // Go to main displayable (canvas)
      cmSave; // Save new sleep time
  private Gauge gaSleep; // Gauge to adjust sleep
  public SleepForm(String title, AnimatedTimer midlet) {
    // Call the form constructor
    super(title);
    // Save reference to MIDlet so we can
    // access the display manager class
    this.midlet = midlet;
    // Commands
    cmSave = new Command("Save", Command.SCREEN, 1);
    cmBack = new Command("Back", Command.BACK, 2);
    cmHome = new Command("Home", Command.SCREEN, 2);
    // Gauge to adjust the length of timer sleep
    gaSleep = new Gauge("Timer Sleep", true, 100, 1000);
    // Set to current sleep. Gauge holds values 0 to 100,
    // divide the current sleep (milliseconds) by 10
    gaSleep.setValue(midlet.cvTimer.getSleep() / 10);
    // Add to form and listen for events
    append(gaSleep);
    addCommand(cmSave);
    addCommand(cmBack);
    addCommand(cmHome);
    setCommandListener(this);
  }
  /*--------------------------------------------------
   * Command event handling
   *-------------------------------------------------*/
  public void commandAction(Command c, Displayable s) {
    if (c == cmSave) {
      // Gauge returns a value between 0 and 100
      // We want milliseconds, so multiply by 10
      midlet.cvTimer.setSleep(gaSleep.getValue() * 10);
      // Return to main midlet
      midlet.displayMgr.home();
    } else if (c == cmBack) {
      // Pop the last displayable off the stack
      midlet.displayMgr.popDisplayable();
    } else if (c == cmHome) {
      // Return to main midlet
      midlet.displayMgr.home();
    }
  }
}
/*--------------------------------------------------
 * Class OptionsList
 *
 * List to provide options for configuring of timer
 *
 * Example from the book:     Core J2ME Technology
 * Copyright John W. Muchow   http://www.CoreJ2ME.ru
 * You may use/modify for any non-commercial purpose 
 *-------------------------------------------------*/
class OptionsList extends List implements CommandListener {
  private AnimatedTimer midlet; // Main midlet
  private Command cmBack;
  public OptionsList(String title, int listType, AnimatedTimer midlet) {
    // Call list constructor
    super(title, listType);
    // Save reference to MIDlet so we can
    // access the display manager class
    this.midlet = midlet;
    // Create the list entries
    append("Sleep interval", null);
    append("Start", null);
    append("Stop", null);
    // Create command and listen for events
    cmBack = new Command("Back", Command.BACK, 1);
    addCommand(cmBack);
    setCommandListener(this);
  }
  /*--------------------------------------------------
   * Command event handling
   *-------------------------------------------------*/
  public void commandAction(Command c, Displayable s) {
    // Event generated by the implicit list
    if (c == List.SELECT_COMMAND) {
      switch (getSelectedIndex()) {
      case 0:
        // Push current displayable and show the form
        // to adjust the timer sleep
        midlet.displayMgr.pushDisplayable(midlet.fmSleep);
        break;
      case 1:
        // Start timer and return to previous displayable
        midlet.cvTimer.startTimer();
        midlet.displayMgr.popDisplayable();
        break;
      case 2:
        // Stop timer and return to previous displayable
        midlet.cvTimer.stopTimer();
        midlet.displayMgr.popDisplayable();
        break;
      }
    } else if (c == cmBack) {
      // Return to previous displayable
      midlet.displayMgr.popDisplayable();
    }
  }
}





Animation MIDlet

/*
J2ME in a Nutshell
By Kim Topley
ISBN: 0-596-00253-X
*/

import java.util.Timer;
import java.util.TimerTask;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.rumand;
import javax.microedition.lcdui.rumandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Gauge;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemStateListener;
import javax.microedition.midlet.MIDlet;
public class AnimationMIDlet extends MIDlet 
                        implements CommandListener, ItemStateListener {
    // The MIDlet"s Display object
    private Display display;
        
    // Flag indicating first call of startApp
    protected boolean started;
    
    // Exit command
    private Command exitCommand;
    
    // Setup command
    private Command setupCommand;
    
    // Run command
    private Command runCommand;
    
    // Configuration form
    private Form form;
    
    // Animation canvas
    private AnimationCanvas canvas;
    
    // Gauge for block count
    private Gauge blockGauge;
    
    // Gauge for frame rate
    private Gauge rateGauge;
    
    // Initial frame rate
    private static final int FRAME_RATE = 1;
    
    // Initial number of blocks
    private static final int BLOCK_COUNT = 1;
    
    protected void startApp() {
        if (!started) {
            display = Display.getDisplay(this);
            form = new Form("Animation");
            rateGauge = new Gauge("Frame rate", true, 10, FRAME_RATE);
            blockGauge = new Gauge("Blocks", true, 4, BLOCK_COUNT);
            form.append(rateGauge);
            form.append(blockGauge);
            form.setItemStateListener(this);
            
            canvas = createAnimationCanvas();            
            
            exitCommand = new Command("Exit", Command.EXIT, 0);
            setupCommand = new Command("Setup", Command.SCREEN, 0);
            runCommand = new Command("Run", Command.SCREEN, 0);
            
            canvas.addCommand(exitCommand);
            canvas.addCommand(setupCommand);
            form.addCommand(exitCommand);
            form.addCommand(runCommand);
            
            form.setCommandListener(this);
            canvas.setCommandListener(this);
            
            display.setCurrent(form);
            started = true;
        }
    }
    protected void pauseApp() {
    }
    protected void destroyApp(boolean unconditional) {
    }    
    public void commandAction(Command c, Displayable d) {
        if (c == exitCommand) {
            // Exit. No need to call destroyApp
            // because it is empty.
            notifyDestroyed();
        } else if (c == runCommand) {
            display.setCurrent(canvas);
        } else if (c == setupCommand) {
            display.setCurrent(form);
        }
    }
    
    public void itemStateChanged(Item item) {
        if (item == blockGauge) {
            int count = blockGauge.getValue();
            if (count < 1) {
                count = 1;
            }
            canvas.setBlockCount(count);
        } else if (item == rateGauge) {
            int count = rateGauge.getValue();
            if (count < 1) {
                count = 1;
            }
            canvas.setFrameRate(count);
        }            
    } 
    
    // Creates the canvas that will draw the block
    protected AnimationCanvas createAnimationCanvas() {
        return new AnimationCanvas();
    }
    
    class AnimationCanvas extends Canvas {
        // Size of each block
        protected static final int SIZE = 4;
        
        // Initial speeds in the X direction
        protected final int[] xSpeeds = { 2, -2, 0, -2 };
        
        // Initial speeds in the Y direction
        protected final int[] ySpeeds = { 2, -2, 2, -0 };
        
        // Background color
        protected int background = display.isColor() ? 0 : 0xc0c0c0;
            
        // Foreground color
        protected int foreground = display.isColor() ? 0xffff00 : 0;
        
        // Width of screen
        protected int width = getWidth();
        
        // Height of screen
        protected int height = getHeight();
        
        // The screen update rate
        protected int frameRate;
        
        // The blocks to draw on the screen
        protected Block[] blocks;
        
        // The update timer
        protected Timer timer;
        
        // The update timer task
        protected TimerTask updateTask;
        
        // Gets the maximum number of blocks
        public int getMaxBlocks() {
            return blocks.length;
        }
        
        // Constructs a canvas with default settings
        AnimationCanvas() {
            setBlockCount(BLOCK_COUNT);
            setFrameRate(FRAME_RATE);
        }
        
        // Sets the number of blocks to draw
        public void setBlockCount(int count) {
            if (count > xSpeeds.length) {
                throw new IllegalArgumentException("Cannot have more than " 
                                + xSpeeds.length + " blocks");
            }
            
            blocks = new Block[count];
            createBlocks();
        }
        
        // Gets the number of blocks to draw
        public int getBlockCount() {
            return blocks.length;
        }
        
        // Sets the number of updates per second
        public void setFrameRate(int frameRate) {
            if (frameRate < 1 || frameRate > 10) {
                throw new IllegalArgumentException("Frame rate must be > 0 and <= 10");
            }
            this.frameRate = frameRate;
            if (isShown()) {
                startFrameTimer();
            }
        }
        
        // Gets the number of updates per second
        public int getFrameRate() {
            return frameRate;
        }  
          
        // Paint canvas background and all
        // of the blocks in their correct locations.
        protected void paint(Graphics g) {
            // Paint with the background color
            g.setColor(background);
            g.fillRect(0, 0, width, height);
            
            // Draw all of the blocks
            g.setColor(foreground);
            synchronized (this) {
                for (int i = 0, count = blocks.length; i < count; i++) {
                    g.fillRect(blocks[i].x, blocks[i].y, SIZE, SIZE);
                }
            }
        }
        
        // Notification that the canvas has been made visible
        protected void showNotify() {
            // Start the frame timer running
            startFrameTimer();
        }
        
        // Notification that the canvas is no longer visible
        protected void hideNotify() {
            // Stop the frame timer 
            stopFrameTimer();
        }
        
        // Creates the blocks to be displayed
        private void createBlocks() {
            int startX = (width - SIZE)/2;
            int startY = (height - SIZE)/2;
            for (int i = 0, count = blocks.length; i < count; i++) {
                blocks[i] = new Block(startX, startY, xSpeeds[i], ySpeeds[i]);
            }
        }
        
        // Starts the frame redraw timer
        protected void startFrameTimer() {
            timer = new Timer();
            
            updateTask = new TimerTask() {
                public void run() {
                    moveAllBlocks();
                }
            };
            long interval = 1000/frameRate;
            timer.schedule(updateTask, interval, interval);
        }
        
        // Stops the frame redraw timer
        protected void stopFrameTimer() {
            timer.cancel();            
        }
        
        // Called on expiry of timer.
        public synchronized void moveAllBlocks() {
            // Update the positions and speeds
            // of all of the blocks
            for (int i = 0, count = blocks.length; i < count; i++) {
                blocks[i].move();
                
                // Request a repaint of the screen
                repaint();                
            }
        }
        
        // Inner class used to represent a block on the screen
        class Block {
            int x;      // X position
            int y;      // Y position
            int xSpeed; // Speed in the X direction
            int ySpeed; // Speed in the Y direction
            
            Block(int x, int y, int xSpeed, int ySpeed) {
                this.x = x;
                this.y = y;
                this.xSpeed = xSpeed;
                this.ySpeed = ySpeed;
            }
            
            void move() {
                x += xSpeed;
                if (x <= 0 || x + SIZE >= width) {
                    xSpeed = -xSpeed;
                }
                
                y += ySpeed;
                if (y <= 0 || y + SIZE >= height) {
                    ySpeed = -ySpeed;
                }                
            }            
        }
    }
}





Animation MIDlet 2

/*
J2ME in a Nutshell
By Kim Topley
ISBN: 0-596-00253-X
*/

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
public class AnimationMIDlet2 extends AnimationMIDlet {
    // Create an AnimationCanvas2
    protected AnimationMIDlet.AnimationCanvas createAnimationCanvas() {
        return new AnimationCanvas2();
    }
    
    class AnimationCanvas2 extends AnimationMIDlet.AnimationCanvas {
        // Override superclass paint method to take
        // into account the clipping rectangle
        protected void paint(Graphics g) {
            // Get the clipping rectange
            int clipX = g.getClipX();
            int clipY = g.getClipY();
            int clipWidth = g.getClipWidth();
            int clipHeight = g.getClipHeight();
            
            // Paint with the background color - only
            // the area within the clipping rectangle
            g.setColor(background);
            g.fillRect(clipX, clipY, clipWidth, clipHeight);
            
            // Draw all of the blocks
            g.setColor(foreground);
            synchronized (this) {
                for (int i = 0, count = blocks.length; i < count; i++) {
                    g.fillRect(blocks[i].x, blocks[i].y, SIZE, SIZE);
                }
            }
        }       
                
        // Called on expiry of timer.
        public synchronized void moveAllBlocks() {
            // Update the positions and speeds
            // of all of the blocks and repaint
            // only the part of the screen that
            // they occupy
            for (int i = 0, count = blocks.length; i < count; i++) {
                // Request a repaint of the current location
                Block block = blocks[i];
                repaint(block.x, block.y, SIZE, SIZE);
                
                blocks[i].move();
                
                // Request a repaint of the new location
                repaint(block.x, block.y, SIZE, SIZE);
            }
        }
    }
}