ActionListener and Event Handling
A javax.swing.Timer exists to generate ActionEvent events at a fixed delay, and it can also be set to fire a single event as an alarm.
Motion Built from Still Pictures
Animation on a screen is an illusion. It is built from a simple ingredient: a series of still pictures shown one after another. When the pictures change quickly and only a little at a time, the eye reads the sequence as one moving object rather than a slideshow. A computer animation is exactly this kind of sequence of still images.
Java needs some way to decide when each new picture should appear. The tool it offers for this job is the javax.swing.Timer, an object that produces a steady beat of events. Each beat gives your program an opportunity to compute the next picture and draw it. This article starts from a Timer-driven animation and works backward to the mechanism that makes it move, so that when something goes wrong you know where to look.
What do you think happens?
You create a Timer and pass it a listener, but you never call start() on it. What happens to the listener's actionPerformed method?
Reveal answer
actionPerformed is never invoked.
A Timer does not start on creation. Until start() is called, the Timer exists but is quiet, so it never announces a moment to the listener. Keep this in mind: the rest of the article shows why that single missing call stops the whole animation.
The Three States of a Timer
Creating a Timer and starting it are two separate steps. A timer does not automatically start generating events when the timer object is created. The start() method must be called to tell the timer to start running. The stop() method turns the stream of events off, and the timer can be restarted later by calling start() again.
A running timer can therefore move through a small cycle: created but idle, running, stopped, and running again. The diagram below traces which method call causes each change. Notice that the idle state and the stopped state are both quiet, since no events flow in either one; only the running state delivers the beat that drives an animation.
How Events Reach actionPerformed
A Timer on its own is not useful. It does not draw, compute, or update any data itself. Its only job is to announce that a moment has arrived. Whatever should happen at that moment has to be written somewhere else, in an object that is set up to listen for those announcements. That object is supplied when the Timer is constructed, along with the delay between events.
The listener must be an object that implements the ActionListener interface. In practice this means its class is declared to implement ActionListener and defines the one method the interface requires, actionPerformed. ActionListener defines just that single method, so the class has exactly one job to satisfy the contract. Each time the Timer generates an event, actionPerformed runs.
FrameListenerdeclares that it implementsActionListener.actionPerformedis the single method the interface requires; this is where per-frame work goes.- The Timer receives the delay and the listener when it is constructed.
start()must be called, or no events are ever generated.
The source describes the same division of labour in words: the Timer supplies the rhythm, and actionPerformed supplies the content of each frame. The sequence diagram below shows the order in which the three parts interact each time an event fires.
Why Stills Become Motion
Two conditions decide whether a viewer perceives continuous motion. The time between images must be short, and the change from one image to the next must not be too great. If both hold, the eye reads the sequence as one moving object. In a Timer-driven animation, the Timer's fixed delay sets the time between images, and the code in actionPerformed sets how much each new frame differs from the last.
Tracing One Animation Loop
Moving a Shape One Step per Tick
A Timer fires every 50 milliseconds. Its listener moves a shape 10 pixels to the right each time actionPerformed runs. The shape starts at x = 0 and the Timer has just been started. What position does the shape show on the first five frames? (The numbers here are illustrative, chosen for this example.)
- 1Start
start()is called, so the Timer moves from idle to running. No frame is drawn yet because no event has fired. - 2Tick 1
The Timer generates an event, the listener's
actionPerformedruns, and the code computes x = 0 + 10 = 10 and draws the shape there. - 3Tick 2
The next event arrives after the same delay.
actionPerformedcomputes x = 10 + 10 = 20 and draws the next still picture. - 4Ticks 3 to 5
The same pattern repeats. The positions drawn are 30, 40, and 50, one picture per event.
- 5Perception
Five still pictures, each about 50 milliseconds apart and each 10 pixels from the last, are read by the eye as one shape sliding steadily to the right.
The frames show x = 10, 20, 30, 40, 50 in turn, and the viewer perceives a continuous rightward motion.
Repeating Timer Versus Alarm Mode
The same Timer class can be configured to behave differently. It can be set to emit a single event after a specified delay, in which case it works like an alarm clock rather than a metronome. For animation, the repeating form is the one that matters, because a moving picture needs a new frame at every tick. The table below contrasts the two modes.
| Aspect | Repeating Timer | Alarm (single event) |
|---|---|---|
| Number of events | One every delay, until stopped | One event after the delay |
| Typical use | Animation, a steady beat of frames | A one-time action after a wait |
| Effect on actionPerformed | Runs again and again | Runs once |
| Stopping the event stream | stop() turns the stream off | Ends on its own after the single event |
Two configurations of the same Timer class, as described in the source.
Mistakes Beginners Make with Timers
Common mistake Assuming the Timer starts when it is created
What it looks likeTimer t = new Timer(50, listener); with no further call, then waiting for the animation to begin.
Why it failsA timer does not automatically start generating events when the timer object is created.
Do this insteadCall start() after construction to begin the event stream.
Common mistake Putting drawing or update logic in the wrong place
Why it failsThe Timer does not draw, compute, or update any data itself. Its only job is to announce that a moment has arrived.
Do this insteadPlace the code that computes and draws each frame inside actionPerformed of the listener.
Common mistake Forgetting that the listener must implement the interface
Why it failsThe listener must be an object that implements
ActionListenerand definesactionPerformed.Do this insteadDeclare the class to implement ActionListener and define actionPerformed with its required signature.
Common mistake Expecting events to continue after stop()
Why it failsstop()turns the stream of events off, and the listener is not invoked untilstart()is called again.Do this insteadCall start() again when the animation should resume.
Finding Where an Animation Diverges
When an animation does not behave as expected, the source suggests splitting the system into three checkpoints and working through them in order. The first one that fails tells you where the divergence happens.
- 1Checkpoint 1: Was the Timer started? If
start()was never called, orstop()was called and not followed bystart(), no events will arrive. - 2Checkpoint 2: Are events arriving at the listener? If the Timer is running but
actionPerformedis not invoked, the listener was not supplied correctly at construction. - 3Checkpoint 3: Does
actionPerformedcompute and draw the intended frame? If events arrive but the picture is wrong, the problem lies in the code inside the listener's method.
An animation shows one still picture and never changes, even though the window opens correctly. Using the three checkpoints, name the first point that could fail and describe what you would check.
Hints
- Start with checkpoint 1. Was
start()called on the Timer? - If
start()was called, check whetheractionPerformedis being invoked at all. - Only if events arrive should you examine what the code inside
actionPerformeddraws.
Key Takeaways
- A
javax.swing.TimergeneratesActionEventevents at a fixed delay, or it can be set to fire a single event as an alarm. - A listener implements
ActionListenerand definesactionPerformed, which runs each time the Timer generates an event; the listener is supplied when the Timer is constructed. - A Timer does not start on creation.
start()begins the event stream, andstop()turns it off untilstart()is called again. - Animation works because a Timer-driven loop shows a sequence of still frames quickly enough, and with small enough changes, that the user perceives continuous motion.
- When an animation misbehaves, check in order whether the Timer was started, whether events reach the listener, and whether
actionPerformedcomputes and draws the intended frame.