Java/J2ME/Animation
Версия от 18:01, 31 мая 2010; (обсуждение)
Содержание
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);
}
}
}
}