Concepts / Event Listeners and ActionEvents

Event Listeners and ActionEvents

The javax.swing Timer class generates a sequence of ActionEvents separated by a specified interval.

  • Programming

Why an Animation Seems to Run by Itself

Earlier in the course, timers were used to drive animations. An animation on screen looks smooth, as if something is moving on its own. This article looks at what a timer actually is and what it is doing while the animation appears to run.

Before you read the explanation, keep one question in mind as you think about any animation loop: when the program is waiting between animation frames, which part of the program is doing the waiting? Your own code, or something else? Commit to a guess now. The rest of the article lets you check that guess against the mechanism rather than against memory.

The Repetition Problem Behind Animation

Many animations are the same small job repeated over and over. Change the picture a little, redraw it, wait, and do it again. The hard part is not the change or the redraw. The hard part is making the repetition happen at regular intervals without freezing the rest of the program while it waits.

Before timers were introduced, threads had to be written by hand to achieve similar periodic behavior. A thread is a separate flow of execution, and writing one yourself meant managing its sleeping, waking, and repeating directly. The timer class exists so that this periodic behavior can be requested without writing that machinery each time.

What do you think happens?

When the program is waiting between two animation frames, which part of the program is doing the waiting?

  • Your listener code, sitting in a loop until the next frame

  • A background thread inside the Timer, which sleeps between events

Reveal answer
Answer:

The background thread inside the Timer does the waiting. Your listener does not run between frames.

A Timer uses a background thread that sleeps most of the time and wakes periodically to generate events. Your listener only performs its work when an event arrives, and the Timer supplies the repetition.

What a Timer Produces

Definition

The javax.swing Timer class generates a sequence of ActionEvents separated by a specified interval. Each event is an ActionEvent, and whatever code you attach as the listener receives those events.

The phrase to focus on is sequence. A Timer does not perform your animation itself. Its job is to produce events at a steady rhythm. The animation is what the listener does each time it is called. In that division of labor, the Timer supplies the repetition and your code supplies the content of each repetition.

The constructor takes two things: the delay between events, and the listener that should be told about each event. Creating the timer does not begin the events. Once the timer is created, calling start tells it to begin producing events. Calling start is the moment the rhythm begins.

Key point

A Timer has two inputs that matter to you: the delay, which sets the gap between ActionEvents, and the listener, which receives every ActionEvent. Your animation logic belongs in the listener.

Where the Waiting Really Happens

A Timer is not free of threads. Behind the scenes, a Timer uses a thread. That thread spends most of its life asleep. Periodically it wakes up, generates the event associated with the timer, and then goes back to sleep until the next interval. The waiting you were asked to locate in the previous section is therefore done by this background thread.

This is why the Timer is a convenient abstraction. The waiting is handled by a background thread that you do not have to write, and your code only has to describe what should happen on each event. You never write a loop that sleeps and checks the clock yourself.

start()timer begins producingeventsTimer thread sleepswaiting for the intervalInterval passesActionEvent generatedListener runs oneframeyour animation stepThread sleeps againuntil the next intervalbeginwaitwakedeliverreturn
During the gap between two frames, which component is waiting? The Timer's background thread is asleep, and the listener only runs when an ActionEvent is generated.

Read the timeline as the answer to the question posed earlier. The sleeping interval belongs to the Timer's background thread. The listener occupies only the short slice after the wake-up, when one frame of work is done. The rest of the time, the listener is not running at all.

Tracing One Animation Frame

Focus on one cycle of the Timer's background thread. The thread is not continuously running your animation. It checks whether the interval has passed, and if it has, it generates an event. Otherwise it keeps sleeping. The sequence diagram below follows one frame from the wake-up through the listener's work and back to sleep.

Timer threadwakes after the intervalListeneractionPerformedPanelrepaint1. deliver ActionEvent2. update state, then repaint3. listener finishes, thread sleeps
For one animation frame, which part of the system acts first, and what does each actor do before control returns to the Timer thread?
  1. 1
    The Timer's background thread wakes up because the interval has passed.
  2. 2
    The thread generates the ActionEvent associated with the timer.
  3. 3
    The event is passed to the listener, which is the code you attached when you created the timer.
  4. 4
    The listener performs one frame of work, such as changing a variable and asking the panel to repaint.
  5. 5
    When the listener finishes, the thread goes back to sleep until the next interval.

How the ActionEvent Reaches the Listener

Each ActionEvent is an object that the Timer creates and hands to the listener. The listener is the piece of code you supply, so the event travels from the Timer's background thread to your code. The path is short: the Timer generates the event, the event is passed on, and your listener receives it and runs.

Timerbackground thread wakesActionEventone event for one intervalListenerreceives the eventYour animation stepruns once per eventgeneratespassed totriggers
How does one ActionEvent move from the Timer to the code that handles it, and which component handles each hop?

Because the listener is called once per event, the number of frames your animation shows is determined by how many events arrive. The Timer decides when events happen, and the listener decides what each event does.

A Sprite Moved by a Timer

The following example uses a sprite whose horizontal position is stored in a variable. The goal is to see exactly which piece of code moves the sprite and which piece makes the change appear on screen. This is an original illustration built on the same pattern described in the source.

Code walkthrough
int x = 0;

Timer timer = new Timer(16, e -> {
    x += 2;
    panel.repaint();
});

timer.start();
  1. 1
    int x = 0; stores the starting horizontal position of the sprite.
  2. 2
    new Timer(16, ...) creates a timer whose events are separated by the delay, and the second argument is the listener that receives each ActionEvent.
  3. 3
    Inside the listener, x += 2 changes the position; this is the one frame of work done per event.
  4. 4
    panel.repaint() asks the panel to draw again so the new position appears on screen.
  5. 5
    timer.start() tells the Timer to begin producing events; nothing moves until this call happens.
  • x holds the sprite's horizontal position
  • The delay of 16 is the gap between ActionEvents
  • The lambda is the listener, called once per event
  • start() begins the stream of events
Output
On each ActionEvent the listener adds 2 to x and requests a repaint. Successive events therefore move the sprite from 0 to 2, then 4, then 6, and so on, with the Timer's background thread sleeping between each step. The program never runs a loop of its own to wait.

Mistakes Beginners Make with Timer Listeners

  • Common mistake

    Putting a sleep call inside the listener to control the speed

    What it looks like
    public void actionPerformed(ActionEvent e) { x += 2; Thread.sleep(500); panel.repaint(); }
    Why it fails

    The Timer already supplies the waiting between events through its background thread. Adding a sleep inside the listener makes the one piece of code that should be short hold the work up for the sleep time, so the code that is supposed to run once per interval is now blocked.

    Do this instead

    Set the delay in the Timer constructor and keep the listener to one short frame of work.

  • Common mistake

    Doing slow work inside the listener

    What it looks like

    A listener that loads a file or performs a long calculation before updating the sprite on every event.

    Why it fails

    The listener is called once per event, so anything slow in it delays the frame it belongs to. The animation then stutters because the frame cannot finish on time.

    Do this instead

    Do the slow work elsewhere and keep the listener to the small change needed for the next frame.

  • Common mistake

    Assuming the listener runs continuously

    Why it fails

    The listener does not run between events. It runs only when an ActionEvent arrives, which is why one frame of work fits inside each call.

    Do this instead

    Think of the listener as a single step that the Timer calls repeatedly, not as a loop that keeps running.

  • Common mistake

    Forgetting to call start

    Why it fails

    The constructor only receives the delay and listener. Without start, the Timer does not begin producing events, so nothing happens.

    Do this instead

    Always call start after creating the Timer.

  • Common mistake

    Confusing the delay with the animation's content: the delay sets the gap between ActionEvents, while the listener decides what the animation does.

Before timers were introduced, programmers had to write the periodic thread themselves. The table below compares that hand-written approach with the Timer at the level the source describes.

ApproachWho writes the waitingWhat the programmer supplies
Hand-written threadThe programmer, who must sleep, wake, and repeat the threadThe thread logic plus the work done each cycle
javax.swing TimerA background thread the Timer providesThe delay and a listener that does one frame of work

Hand-written threads compared with the javax.swing Timer, following the source's description

Practice: Locate the Waiting

MEDIUM

A teammate's Timer listener calls Thread.sleep(1000) before updating a sprite, and the animation looks slow and stuttery while the rest of the window stops responding. Explain which part of the program is supposed to do the waiting, where your teammate has placed the waiting instead, and what change would restore the intended behavior. Use the terms ActionEvent, listener, and background thread in your answer.

Hints
  • Ask which component the source says sleeps between events.
  • Compare where the listener runs with where the Timer's thread sleeps.
  • Think about what the listener should contain according to the one-frame pattern.

Key Takeaways

  • The javax.swing Timer class generates a sequence of ActionEvents separated by a specified interval, and it is created with a delay and a listener and started with start.
  • The waiting in a Timer-driven animation is done by a background thread that sleeps most of the time and wakes periodically to generate events.
  • Each time an ActionEvent arrives, the listener performs one frame of animation, so the listener should stay short.
  • A sleep or slow work inside the listener blocks the frame it belongs to and undermines the steady rhythm the Timer provides.