Concepts / Implementing Event Listener Interfaces

Implementing Event Listener Interfaces

A component that must react to input focus learns about changes through FocusEvent notifications.

  • Programming

A Field That Should Light Up

Many user interface components look different depending on whether the user is typing into them or is about to type into them. A text field that is ready for input might show a highlighted background, and that highlight should disappear when the user moves on. For the component to change its appearance at the right moment, it needs a way to learn that it has received the input focus and a way to learn that it has lost it. Java provides this through events. This article follows that notification from start to finish, so you can see exactly where the component learns about the change and where your own code gets a chance to respond.

What do you think happens?

A text field has a listener registered with it. The user clicks into the field. What does Java do to tell your code about this change?

  • Your code keeps checking the field to see whether it has focus

  • The component calls focusGained on each registered listener

  • The component calls focusLost on each registered listener

Reveal answer
Answer:

The component calls focusGained on each registered listener.

Your code does not poll the component. The component pushes a notification at the moment the change happens, and the call it makes for a gain of focus is focusGained.

How a Focus Change Reaches Your Code

A focus change is reported to interested objects as an event. In Java, that event has the type FocusEvent. You can think of a FocusEvent as a short message that says the input focus has moved. The message is delivered only to objects that have agreed to receive it. The key idea is the direction of the flow: the component pushes a notification to listeners at the moment the change happens. Your listener does not repeatedly ask the component whether it currently has focus.

The notification does not travel from the component to the listener by magic. The component performs a specific sequence each time its focus status changes. When it gains the input focus, it calls focusGained on every FocusListener registered with it. When it loses the focus, it calls focusLost on every registered listener instead. The component is the one doing the calling, and the listeners are the ones being called. This is why the component is the place to look when a listener seems to be silent.

Focus changeComponentRegistered listenerfocus status changesfocusGained or focusLost, each with a FocusEvent
When focus moves to or away from a component, which object calls which method, and in what order?

Read this flow as two independent paths that both begin at a focus change. A gain produces one kind of call and a loss produces the other. Nothing in this dispatch step depends on what your listener does with the call. The component only guarantees that the call is made.

The FocusListener Contract

An object that wants to hear about focus changes can implement the FocusListener interface. An interface is a contract: any class that claims to implement it must supply the methods the contract lists. The FocusListener interface declares exactly two methods, one for each direction of focus change. The first is called when the component gains focus, and the second is called when it loses focus. Both receive a FocusEvent as their parameter.

The two method signatures match in every way except their names. The name is the only thing that tells the component which situation is being reported. Because the interface requires both methods, a class that implements FocusListener must write bodies for both, even if one of them does nothing.

java
Walk through it
  1. 1
    The import lines bring in the FocusEvent and FocusListener types.
  2. 2
    The class declaration names FocusListener after implements, which obliges the class to provide both methods.
  3. 3
    Each method takes one FocusEvent parameter and is called by the component, not by your code.
  4. 4
    The bodies are empty here, but both must exist for the class to compile.
Notes on the code
  • implements FocusListener is the promise to supply the interface's methods
  • focusGained is the method for a gain of focus
  • focusLost is the method for a loss of focus
  • The FocusEvent parameter is the notification value passed in on each call

Registering the Listener

Implementing the interface is only half of the arrangement. A listener object must also be connected to the component it cares about. The source is explicit that the addFocusListener method must be used to set up a listener for focus events. Calling it on a component adds the listener to that component's collection of focus listeners. Until that call happens, the listener exists as an object but never receives anything, because the component has no record of it.

java
Notes on the code
  • The component is created first, because the listener needs a target
  • addFocusListener adds the listener object to the component's collection
JTextFieldListener objectFocusListenerimplementskeeps in focus listener collection
How the component, the registered listener object, and the FocusListener interface are connected, and what the component keeps a reference to.

The diagram shows the key relationship. One component can have many registered listeners, and each listener receives the FocusEvent as an argument when it is called. The component holds the listeners in its collection, and the interface is the shared shape that all of those listener objects must follow.

Tracing a Highlighted Text Field

The following example is an original illustration built from the pattern described above. It uses a text field whose background turns yellow while it has focus and returns to white when focus leaves. The anonymous class implements both methods of FocusListener, and the call to addFocusListener performs the registration that makes those methods reachable.

java
Notes on the code
  • The anonymous class supplies both required methods in one place
  • focusGained turns the background yellow when the field gains focus
  • focusLost restores the background to white when focus leaves
  • addFocusListener is the registration step that connects the listener to nameField
Worked example

Tracing focus from one field to another

The user has the name field highlighted and presses Tab to move to an email field. The name field has the highlighting listener registered. Which method runs on that listener, and what is the resulting appearance of the name field?

  1. 1
    Identify the transition

    The name field goes from having focus to not having focus. That is a loss of focus for the name field.

  2. 2
    Find the matching method

    The component calls focusLost on each registered listener when it loses focus, so focusLost on the name field's listener runs.

  3. 3
    Trace the body

    The focusLost body calls setBackground with Color.WHITE, so the name field returns to a white background.

  4. 4
    Consider the other field

    The email field is gaining focus. If it had a listener like this one, its focusGained method would run. The source does not specify which of the two calls is made first, so the trace should not assume an order between them.

Result

focusLost runs on the name field's listener, and the name field's background returns to white.

Field A has focusField B has no focusField A has nofocusField B has focusfocusLost on AfocusGained on B
When focus moves from field A to field B, what is the state of each component before and after, and which listener method is called on each?

The two events are mirror images, and it helps to keep them side by side. Each is triggered by a different transition, calls a different method, and is usually paired with the opposite change so that the component returns to a sensible appearance when focus moves on. In the diagram, field A makes the focusLost call and field B makes the focusGained call. Each field's listener only handles the direction it was told about.

Mistakes Beginners Make with Focus Listeners

  • Common mistake

    Writing the listener class but never registering it

    What it looks like

    A class implements FocusListener correctly, but the code never calls addFocusListener on the component.

    Why it fails

    The component can only call listeners it knows about. Without the call, the listener exists as an object but never receives a notification.

    Do this instead

    Call addFocusListener on the component with the listener object. When a silent focus handler is being debugged, check this registration first.

  • Common mistake

    Implementing only one of the two methods

    What it looks like

    A class implements FocusListener and writes focusGained, but leaves focusLost out.

    Why it fails

    The interface requires both methods, so the class does not satisfy its contract.

    Do this instead

    Write both methods. If one direction needs no action, give that method an empty body.

  • Common mistake

    Expecting focusGained to run when focus is lost

    What it looks like

    A developer expects the highlight to be removed by focusGained when the user leaves the field.

    Why it fails

    The name is the only thing that tells the component which situation is being reported. A loss of focus calls focusLost.

    Do this instead

    Put the code that removes the highlight in focusLost, and the code that adds it in focusGained.

  • Common mistake

    Polling the component for focus instead of waiting for the call

    What it looks like

    Code repeatedly checks whether the field currently has focus to decide whether to highlight it.

    Why it fails

    The component pushes a notification when the change happens. Polling works against that design.

    Do this instead

    Place the reaction inside focusGained and focusLost, and let the component call them.

Checking Your Trace

MEDIUM

A component has two registered FocusListener objects, called first and second. The component loses focus. Using the rule that the component calls focusLost on every registered listener, state which methods run and on which objects. Then say what you would check first if the second listener never seemed to react.

Hints
  • Count how many listeners are registered before you trace the calls
  • The component is the caller and the listeners are the ones being called
  • Think about the registration step if a listener is silent

Key Takeaways

  • A component learns about input focus changes through FocusEvent notifications, which it pushes to listeners at the moment of the change.
  • A FocusListener implementation must supply both focusGained and focusLost, and each receives a FocusEvent parameter.
  • The component calls focusGained when it gains focus and focusLost when it loses focus, on every registered listener.
  • A listener must be registered with addFocusListener, because the component can only notify listeners it knows about.