Start of Tutorial > Start of Trail > Start of Lesson | Search |
Every program that performs animation by painting at regular intervals needs an animation loop. Generally, this loop should be in its own thread. It should never be in thepaintComponent
method, since that would take over the event-dispatching thread, which is in charge of painting and event handling.The
Timer
class makes implementing an animation loop easy. This section provides twoTimer
-based templates for performing animation, one for applets and another for applications. The applet version is pictured below. When running the applet, you can click on it to stop the animation. Click again to restart it.The animation the template performs is a bit boring: it simply displays the current frame number, using a default rate of 10 frames per second. The next few sections build on this example, showing you how to animate images.
This is a picture of the applet's GUI. To run the applet, click the picture. The applet will appear in a new browser window.You can find the code for the applet version of the animation template in
AnimatorAppletTimer.java
. The code for the application version is inAnimatorApplicationTimer.java
. The rest of this section explains the templates' common code. Here is a summary of what both templates do:public class AnimatorClass ... implements ActionListener { int frameNumber = -1; Timer timer; boolean frozen = false; JLabel label; //In initialization code: //From user-specified frames-per-second value, determine //how long to delay between frames. ... //Set up a timer that calls this object's action handler. timer = new Timer(delay, this); ... //Set up the components in the GUI. public synchronized void startAnimation() { ... timer.start(); ... } public synchronized void stopAnimation() { ... timer.stop(); ... } public void actionPerformed(ActionEvent e) { //Advance the animation frame. frameNumber++; //Request that the frame be painted. label.setText("Frame " + frameNumber); } ... //When the application's GUI appears: startAnimation(); ... }
The animation templates use four instance variables. The first (frameNumber
) represents the current frame. It's initialized to -1, even though the first frame number is 0. The reason: the frame number is incremented at the start of the animation loop, before the first frame is painted. Thus, the first frame to be painted is frame 0.The second instance variable (
timer
) is theTimer
object that implements the animation loop. It's initialized to fire an action event everydelay
milliseconds.The
delay
variable is a local variable that's initialized using a frames per second number provided by the user. The following code converts frames per second into the number of milliseconds between frames:delay = (fps > 0) ? (1000 / fps) : 100;The
?
:
notation in the previous code snippet is shorthand forif
else
. If the user provides a number of frames per second greater than 0, then the delay is 1000 milliseconds divided by the number of frames per second. Otherwise, the delay between frames is 100 milliseconds (ten frames per second).The third instance variable (
frozen
) is a boolean value that's initialized tofalse
. The templates set it totrue
when the user requests that the animation stop. You'll see more about this later in this section.The fourth instance variable (
label
) is a reference to the component that performs the painting.
TheTimer
object implements the animation loop by continuously firing action events everydelay
milliseconds. In response to each action event, theactionPerformed
method does the following:
- Advances the frame number.
- Requests that the current frame of animation be painted.
For more information about timers, see How to Use Timers.
Two more features of the animation templates belong in the category of polite behavior.The first feature is allowing the user to explicitly stop (and restart) the animation, while the applet or application is still visible. Animation can be quite distracting, and it's a good idea to give the user the power to stop the animation so that the user can concentrate on something else. This feature is implemented by overriding the
mousePressed
method so that it stops or starts the timer, depending on the program's current state. Here's the code that implements this:...//In initialization code: boolean frozen = false; ... public synchronized void startAnimation() { if (frozen) { //Do nothing. The user has requested that we //stop changing the image. } else { //Start animating! ... timer.start(); ... } public synchronized void stopAnimation() { ... timer.stop(); ... } ... //In a mouse listener registered on the animating component: public void mousePressed(MouseEvent e) { if (frozen) { frozen = false; startAnimation(); } else { frozen = true; stopAnimation(); } }The second feature is suspending the animation whenever the applet or application is known not to be visible. For the applet animation template, this is achieved by implementing the
Applet
stop
andstart
methods to callstopAnimation
andstartAnimation
, respectively. For the application animation template, this is achieved by implementing a window event handler that reacts to iconification and deiconification by, again, callingstopAnimation
andstartAnimation
, respectively.In both templates, if the user hasn't frozen the animation, then when the program detects that the animation isn't visible, it tells the timer to stop. When the user revisits the animation, the program restarts the timer, unless the user has explicitly requested that the animation be stopped.
Start of Tutorial > Start of Trail > Start of Lesson | Search |