Você está na página 1de 8

Animating applets using threads

A simple applet
An applet is a small application running inside another application, like a plug-in. A Java applet is
an application running inside a Web page, that is, inside a browser. Java applets provide animation
and interactive features to Web pages. For example, they can provide the animation of a picture
and capture mouse inputs to control the animation. The main difference between a Java applet and
a Java script based Web application consists in the fact that a Java applet executes as a Java
bytecode, while a Java script executes on the fly from the Web pages. A Java applet must be first
compiled with a Java compiler and deployed on some web server. A Java script is inserted in the
HTML documents as source code.
The following example is the Java code of a simple applet. It draws a small rectangle.
import java.applet.Applet;
import java.awt.*;
public class SimpleApplet extends Applet {
public void paint(Graphics g) {
// Print a message on the screen (x=20, y=10).
g.drawString("A simple applet", 20, 10);
// Draws a rectangle on the screen (x=40, y=30).
g.drawRect(40, 30, 20, 20);
// Print a message on the screen (x=20, y=80).
g.drawString("that draws a rectangle.", 20, 80);
}
}
. As we can see an applet does not have a main() method. It has a paint(Graphics g) method
instead. This method takes an argument that is a Graphics object. We can not call this method
directly. (You can try to call it and you'll face the problem of getting the argument g). But we can call it
indirectly via a call to repaint() method, that has no argument.
This class extends the Applet class and, thus, inherits the following methods from this class init(),
paint(), start(), stop(), and destroy() that are handled automatically when needed. These
method are needed for the life cycle of the applet because an applet interacts with the browser
events. However, none of these methods do anything, although their meaning are straightforward
from their names. If we need something to happen with these methods in an applet, we have to
override them with new versions in your applet program.
This applet code should be saved in a file named as "SimpleApplet.java" and compiled. The
resulting SimpleApplet.class file should be copied on a web sever for Internet access or on a local
file system for local access. This file, however, can not be loaded directly by a web browser. It must
be loaded from within an HTML page by using the <APPLET> or the <OBJECT> tag. So, to load
this applet class, we have to create first a HTML file SimpleApplet.html, for example, with the
following content:
<HTML>
<HEAD>
<TITLE>SimpleApplet_example.html</TITLE>
</HEAD>
<BODY>
<H1>A Java applet example</H1>
<APPLET code="SimpleApplet.class" WIDTH="200" HEIGHT="100">
This is a simple applet.</APPLET></P>
</BODY>
</HTML>
To see how this applet looks in the HTML page we have to open it in a browser or in an
appletviewer. If we decide to use the Sun applet viewer, we have to run this command line:
Animating applets using threads 23/09/14
http://www.ase.md/~aursu/AppletsAnimationThreads.html 1 / 8
> appletviewer SimpleApplet.html
This applet will draw a small rectangle.
What is the animation of an applet and why it requires
threads ?
The animation of an applet consists in updating it multiple times per second. The animation can be
carried out in a loop which repaints the applet with small delays between the paint() method
invocations.
A common mistake is to put the animation loop in the paint() method of the applet. Doing so we
will have strange side effects because it holds up the main thread, which is in charge of all drawing
and event handling.
For example, consider the following applet the main thread of which tries to update the applet in the
paint() method loop.
//Example 18.
import java.awt.*;
import java.applet.Applet;
public class AppletAnimatorErroneous extends java.applet.Applet {
int frame;
int delay;

// Initialize the applet and compute the delay between frames.
public void init() {
String str = getParameter("fps");
int fps = (str != null) ? Integer.parseInt(str) : 10;
delay = (fps > 0) ? (1000 / fps) : 100;
}

public void paint(Graphics g) {

// Remember the starting time
long startTime = System.currentTimeMillis();

//This is the animation loop.
while (true) {
//Advance the animation frame.
frame++;

//Clean the display region.
g.drawString(" ", 30, 30);
//Display the frame number.
g.drawString("Frame: " + frame, 30, 30);

//Delay depending on how far we are behind.
try {
startTime +=delay;
Thread.sleep(Math.max(0, startTime-System.currentTimeMillis()));
} catch (InterruptedException e) {
break;
}
}
}
}
Trying to run this applet using the HTML page:
<applet code=AppletAnimatorErroneous.class width=200 height=200>
<param name=fps value=20>
</applet>
we will see strange side effects like freezing of the applet window and locking the applet's controls
when starting the applet with the appletviewer. All these effects are caused by the fact that the
paint() method holds up the main AWT thread, which is in charge of all drawing and event
handling.
Animating applets using threads 23/09/14
http://www.ase.md/~aursu/AppletsAnimationThreads.html 2 / 8
Using threads to animate applets
The main idea of the applets animation is to use a new Java thread that contains an animation loop.
The animation loop is responsible for keeping track of the current frame and for requesting periodic
screen updates. To implement a thread, we must either create a subclass of Thread or adhere to
the Runnable interface.
Consider the following Arthur van Hoff's example:
//Example 19.
import java.awt.*;
import java.applet.Applet;
public class AppletAnimatorSimple extends java.applet.Applet implements Runnable {
int frame;
int delay;
Thread animator;

// Initialize the applet and compute the delay between frames.
public void init() {
String str = getParameter("fps");
int fps = (str != null) ? Integer.parseInt(str) : 10;
delay = (fps > 0) ? (1000 / fps) : 100;
}

// This method is called when the applet becomes visible on
// the screen. Create a thread and start it.
public void start() {
animator = new Thread(this);
animator.start();
}
// This method is called when the applet is no longer
// visible. Set the animator variable to null so that the
// thread will exit before displaying the next frame.

public void stop() {
animator = null;
}

// This method is called by the thread that was created in
// the start method. It does the main animation.

public void run() {
// Remember the starting time
long startTime = System.currentTimeMillis();
//This is the animation loop.

while (Thread.currentThread() == animator) {
//Advance the animation frame.
frame++;

//Display it.
repaint();

//Delay depending on how far we are behind.
try {
startTime +=delay;
Thread.sleep(Math.max(0, startTime-System.currentTimeMillis()));
} catch (InterruptedException e) {
break;
}
}
}

//Draw the current frame of animation.
public void paint(Graphics g) {
g.drawString("Frame: " + frame, 30, 30);
}
}
The example above is free of the lacks of the previous example. The "smooth" updating of the
Animating applets using threads 23/09/14
http://www.ase.md/~aursu/AppletsAnimationThreads.html 3 / 8
screen is achieved by a concurrent thread that "sleeps" and awakes periodically to invoke the
repaint() method which in turn calls the paint() method.
A bit modified example which allows to start and stop the animation clicking on the mouse is
presented below.
//Example 20.
import java.awt.*;
import java.applet.Applet;
// Based on the previous animation example, this applet
// allows to start and stop the animation clicking on the mouse.
public class AppletAnimatorStartStop extends Applet implements Runnable {
int frame;
int delay;
Thread animator;
boolean frozen = false;

// Initialize the applet and compute the delay between frames.
public void init() {
String str = getParameter("fps");
int fps = (str != null) ? Integer.parseInt(str) : 10;
delay = (fps > 0) ? (1000 / fps) : 100;
}

public void start() {
if (frozen) {
// Do nothing. The user has requested that we
// stop changing the image.
} else {
// Start animating!
if (animator == null) {
animator = new Thread(this);
}
animator.start();
}
}

public void stop() {
// Stop the animation thread.
animator = null;
}

public boolean mouseDown(Event e, int x, int y) {
if (frozen) {
frozen = false; start();
} else {
frozen = true; stop();
}
return true;
}

// This method is called by the thread that was created in
// the start method. It does the main animation.
public void run() {
// Just to be nice, lower this thread's priority
// so it can't interfere with other processing going on.
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);

// Remember the starting time.
long startTime = System.currentTimeMillis();

// This is the animation loop.
while (Thread.currentThread() == animator) {

// Advance the animation frame.
frame++;

// Display it.
repaint();

//Delay depending on how far we are behind.
try {
Animating applets using threads 23/09/14
http://www.ase.md/~aursu/AppletsAnimationThreads.html 4 / 8
startTime +=delay;
Thread.sleep(Math.max(0, startTime-System.currentTimeMillis()));
} catch (InterruptedException e) {
break;
}
}
}

// Draw the current frame of animation.
public void paint(Graphics g) {
g.drawString("Frame: " + frame, 30, 30);
}
}
A common problem that can arise in animating applets is the flashing of the animated image. For
example, consider the following applet, which makes the string "Hello World!" run in a window.
//Example 21.
import java.awt.*;
import java.applet.Applet;
// This applet animates a running string "Hello World!"
// It may cause flashing of the animated string.
public class AppletAnimatorRunningStringFlash extends Applet implements Runnable {
int frame;
int delay;
Thread animator;
boolean frozen = false;
int position = 0;

// Initialize the applet and compute the delay between frames.
public void init() {
String str = getParameter("fps");
int fps = (str != null) ? Integer.parseInt(str) : 10;
delay = (fps > 0) ? (1000 / fps) : 100;
}

public void start() {
if (frozen) {
// Do nothing. The user has requested that we
// stop changing the image.
} else {
// Start animating!
if (animator == null) {
animator = new Thread(this);
}
animator.start();
}
}
public void stop() {
// Stop the animation thread.
animator.stop();
animator = null;
}

public boolean mouseDown(Event e, int x, int y) {
if (frozen) {
frozen = false; start();
} else {
frozen = true; stop();
}
return true;
}

// This method is called by the thread that was created in
// the start method. It does the main animation.
public void run() {
// Just to be nice, lower this thread's priority
// so it can't interfere with other processing going on.
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);

// Remember the starting time.
Animating applets using threads 23/09/14
http://www.ase.md/~aursu/AppletsAnimationThreads.html 5 / 8
long startTime = System.currentTimeMillis();

// This is the animation loop.
while (Thread.currentThread() == animator) {

// Advance the animation frame.
frame++;
position++;

// Display it.
repaint();

//Delay depending on how far we are behind.
try {
startTime +=delay;
Thread.sleep(Math.max(0, startTime-System.currentTimeMillis()));
} catch (InterruptedException e) {
break;
}
}
}

// Draw the current frame of animation.
public void paint(Graphics g) {
Font font = new Font("Courier", Font.BOLD, 48);
g.setFont(font);
g.drawString("Hello World !", position, 80);
}
}
The exists a problem when animating large images. The flashing you may see in the example above
may have two causes: painting each frame takes too long (due to the amount of computation that is
required during the repaint) and the entire background is cleared before paint() method is called.
While the computation of the next frame is going on, the user is seeing the background of the
animation. On some platforms like the PC, the flashing is stronger than it is on X Windows. The
reason is that the X Windows graphics are buffered, which makes the flash a little shorter.
The exits two methods to reduce flashing: implementing the update() method and using double
buffering (sometimes known as using a backbuffer).
Overriding the update() method
When the AWT receives a repaint request for an applet, it calls the applet's update() method.
By default, the update() method clears the applet's background and then calls the paint()
method. By overriding the update() method to include the drawing code that used to be in the
paint() method, you avoid having the applet's entire area cleared with every repaint. Now
that the background is no longer cleared automatically, you need to do it yourselves in the
update() method.
Whenever you override the update() method, you still need to implement paint(). This is
because the paint() method is called directly by the AWT drawing system whenever
"damage" occurs to the applet's drawing area -- for example, when a window obscuring part
of the applet's drawing area is removed from the screen. Your paint() implementation can
simply call update().
Double Buffering
Another way of reducing the flashing between frames is to use double buffering. This
technique is used in many animation applets. The general principle is that you create an
offscreen image, you draw a frame into the image, and then you slap the entire image onto the
screen with one call to drawImage(). The advantage is that most of the drawing is done
offscreen. The final painting of the offscreen image onto the screen is usually much more
efficient than painting the frame directly to the screen. The only disadvantage is that you have
to allocate an offscreen image that is as large as the drawing area. If the drawing area is very
large, this may demand quite a lot of memory.
The example below avoids flashing of the running string using both methods: overriding the
update() method and using double buffering.
//Example 22.
Animating applets using threads 23/09/14
http://www.ase.md/~aursu/AppletsAnimationThreads.html 6 / 8
import java.awt.*;
import java.applet.Applet;
public class AppletAnimatorRunningStringDoubleBuffering extends Applet implements Runnable {
int frame;
int delay;
Thread animator;
boolean frozen = false;

Dimension offDimension;
Image offImage;
Graphics offGraphics;
int position = 0;

// Initialize the applet and compute the delay between frames.
public void init() {
String str = getParameter("fps");
int fps = (str != null) ? Integer.parseInt(str) : 10;
delay = (fps > 0) ? (1000 / fps) : 100;
}

public void start() {
if (frozen) {
// Do nothing. The user has requested that we
// stop changing the image.
} else {
// Start animating!
if (animator == null) {
animator = new Thread(this);
}
animator.start();
}
}

public void stop() {
// Stop the animation thread.
animator = null;
offImage = null;
offGraphics = null;
}

public boolean mouseDown(Event e, int x, int y) {
if (frozen) {
frozen = false; start();
} else {
frozen = true; stop();
}
return true;
}

// This method is called by the thread that was created in
// the start method. It does the main animation.
public void run() {
// Just to be nice, lower this thread's priority
// so it can't interfere with other processing going on.
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);

// Remember the starting time.
long startTime = System.currentTimeMillis();

// This is the animation loop.
while (Thread.currentThread() == animator) {

// Advance the animation frame.
frame++;
position++;

// Display it.
repaint();

//Delay depending on how far we are behind.
try {
startTime +=delay;
Animating applets using threads 23/09/14
http://www.ase.md/~aursu/AppletsAnimationThreads.html 7 / 8
Thread.sleep(Math.max(0, startTime-System.currentTimeMillis()));
} catch (InterruptedException e) {
break;
}
}
}

// Update a frame of animation.
public void update(Graphics g) {
Dimension d = size();

// Create the offscreen graphics context
if ((offGraphics == null)
|| (d.width != offDimension.width)
|| (d.height != offDimension.height)) {
offDimension = d;
offImage = createImage(d.width, d.height);
offGraphics = offImage.getGraphics();
offGraphics.setColor(getBackground());
offGraphics.fillRect(0, 0, d.width, d.height);
offGraphics.setColor(Color.black);
Font font = new Font("Courier", Font.BOLD, 48);
offGraphics.setFont(font);
offGraphics.drawString("Hello World!", 0, 80);
}
// Erase the previous image
offGraphics.setColor(getBackground());
offGraphics.fillRect(0, 0, d.width, d.height);
offGraphics.setColor(Color.black);
// Paint the frame into the image
paintFrame(offGraphics);

// Paint the image onto the screen
g.drawImage(offImage, 0, 0, null);
}

// Paint the previous frame (if any).
public void paint(Graphics g) {
if (offImage != null) {
g.drawImage(offImage, 0, 0, null);
}
}

// Draw the current frame of animation.
public void paintFrame(Graphics g) {
Font font = new Font("Courier", Font.BOLD, 48);
g.setFont(font);
g.drawString("Hello World !", position, 80);
}
}
Exercises
1. Study and try all the examples presented above.
2. Write an applet which draws a running string "Hello World!" in a circular way. The animation
must be achieved by a background thread. The applet must avoid flashing and it must allow to
start and stop the animation using mouse clicks.
Useful links and credits:
Animation in Java Applets
Last Revised:
Back to Concurrent Programming Page
Animating applets using threads 23/09/14
http://www.ase.md/~aursu/AppletsAnimationThreads.html 8 / 8

Você também pode gostar