Implementing Event Listener Interfaces
A component that must react to input focus learns about changes through FocusEvent notifications.
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?
Reveal 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.
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.
- 1The import lines bring in the
FocusEventandFocusListenertypes. - 2The class declaration names
FocusListenerafter implements, which obliges the class to provide both methods. - 3Each method takes one
FocusEventparameter and is called by the component, not by your code. - 4The bodies are empty here, but both must exist for the class to compile.
- implements
FocusListeneris the promise to supply the interface's methods focusGainedis the method for a gain of focusfocusLostis the method for a loss of focus- The
FocusEventparameter 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.
- The component is created first, because the listener needs a target
addFocusListeneradds the listener object to the component's collection
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.
- The anonymous class supplies both required methods in one place
focusGainedturns the background yellow when the field gains focusfocusLostrestores the background to white when focus leavesaddFocusListeneris the registration step that connects the listener tonameField
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?
- 1Identify the transition
The name field goes from having focus to not having focus. That is a loss of focus for the name field.
- 2Find the matching method
The component calls
focusLoston each registered listener when it loses focus, sofocusLoston the name field's listener runs. - 3Trace the body
The
focusLostbody callssetBackgroundwith Color.WHITE, so the name field returns to a white background. - 4Consider the other field
The email field is gaining focus. If it had a listener like this one, its
focusGainedmethod 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.
focusLost runs on the name field's listener, and the name field's background returns to white.
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 likeA class implements FocusListener correctly, but the code never calls addFocusListener on the component.
Why it failsThe component can only call listeners it knows about. Without the call, the listener exists as an object but never receives a notification.
Do this insteadCall 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 likeA class implements FocusListener and writes focusGained, but leaves focusLost out.
Why it failsThe interface requires both methods, so the class does not satisfy its contract.
Do this insteadWrite 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 likeA developer expects the highlight to be removed by focusGained when the user leaves the field.
Why it failsThe name is the only thing that tells the component which situation is being reported. A loss of focus calls
focusLost.Do this insteadPut 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 likeCode repeatedly checks whether the field currently has focus to decide whether to highlight it.
Why it failsThe component pushes a notification when the change happens. Polling works against that design.
Do this insteadPlace the reaction inside focusGained and focusLost, and let the component call them.
Checking Your Trace
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
FocusEventnotifications, which it pushes to listeners at the moment of the change. - A
FocusListenerimplementation must supply bothfocusGainedandfocusLost, and each receives aFocusEventparameter. - The component calls
focusGainedwhen it gains focus andfocusLostwhen it loses focus, on every registered listener. - A listener must be registered with
addFocusListener, because the component can only notify listeners it knows about.