Tables 16-1 and 16-2
summarize commonly used Swing events, which Swing components fire them,
and the methods of the listener interfaces that receive them. The events
and listeners are divided between the packages java.awt.event
and
javax.swing.event
.
Table 16-1. Swing component and container events
Event | Fired by | Listener interface | Handler methods |
---|---|---|---|
All components | | ||
All components | | ||
All components | | ||
All components | | ||
| |||
All containers | |
Table 16-2. Component-specific swing events
Event | Fired by | Listener interface | Handler method |
---|---|---|---|
|
| ||
| |||
| |||
| |||
| |||
| | ||
| |||
| |||
| |||
| |||
| |||
| |||
| | ||
| |||
| |||
| |||
| | ||
| | ||
| |||
[a] The |
In Swing, a component’s model and view are distinct. Strictly
speaking, components don’t fire events; models do. When you press a
JButton
, for example, it’s actually the
button’s data model that fires an ActionEvent
, not the button itself. But JButton
has a convenience method for registering
ActionListener
s; this method passes its
argument through to register the listener with the button model. In many
cases (as with JButton
s), you don’t
have to deal with the data model separately from the view, so we can speak
loosely of the component itself firing the events. InputEvent
s are, of course, generated by the
native input system and fired for the appropriate component, although the
listener responds as though they’re generated by the component.
It’s not ideal to have your application components implement a bunch of listener interfaces and receive events directly. Sometimes it’s not even possible. Being an event receiver forces you to modify or subclass your objects to implement the appropriate event listener interfaces and add the code necessary to handle the events. And because we are talking about Swing events here, a more subtle issue is that you would be, of necessity, building GUI logic into parts of your application that shouldn’t have to know anything about the GUI. Let’s look at an example.
In Figure 16-4, we drew the plans
for our Vegomatic food processor. We made our Vegomatic
object implement the ActionListener
interface so that it can
receive events directly from the three JButton
components: Chop
, Puree
, and Frappe
. The problem is that our Vegomatic
object now has to know more than how
to mangle food. It also has to be aware that it is driven by three
controls—specifically, buttons that send action commands—and be aware of
which methods it should invoke for those commands. Our boxes labeling
the GUI and application code overlap in an unwholesome way. If the
marketing people should later want to add or remove buttons or perhaps
just change the names, we have to be careful. We may have to modify the
logic in our Vegomatic
object. All is
not well.
An alternative is to place an adapter class between our event source and receiver. An adapter is a simple object whose sole purpose is to map an incoming event to an outgoing method.
Figure 16-5 shows a better design that uses three adapter classes, one for each button. The implementation of the first adapter might look like:
class
VegomaticAdapter1
implements
ActionListener
{
Vegomatic
vegomatic
;
VegomaticAdapter1
(
Vegomatic
vegomatic
)
{
this
.
vegomatic
=
vegomatic
;
}
public
void
actionPerformed
(
ActionEvent
e
)
{
vegomatic
.
chopFood
();
}
}
So somewhere in the code where we build our GUI, we could register our listener like this:
Vegomatic
theVegomatic
=
...;
Button
chopButton
=
...;
// make the hookup
chopButton
.
addActionListener
(
new
VegomaticAdapter1
(
theVegomatic
)
);
Instead of registering itself (this
) as the Button
’s listener, the adapter registers the
Vegomatic
object (theVegomatic
). In this way, the adapter acts
as an intermediary, hooking up an event source (the button) with an
event receiver (the virtual chopper).
We have completely separated the messiness of our GUI from the application code. However, we have added three new classes to our application, none of which does very much. Is that good? It depends on your vantage point.
Under different circumstances, our buttons may have been able to
share a common adapter class that was simply instantiated with different
parameters. Various tradeoffs can be made between size, efficiency, and
elegance of code. Adapter classes will often be generated automatically
by development tools. The way we’ve named our adapter classes VegomaticAdapter1
, VegomaticAdapter2
, and VegomaticAdapter3
hints at this. More often,
when handcoding, you’ll use an anonymous inner class, as we’ll see in
the next section. At the other extreme, we can forsake Java’s strong
typing and use the Reflection API to create a completely dynamic hookup
between an event source and its listener.
Many listener interfaces contain more than one event handler method. Unfortunately, this means that to register yourself as interested in any one of those events, you must implement the whole listener interface. To accomplish this, you might find yourself typing dummy “stubbed-out” methods to complete the interface. There is nothing wrong with this, but it is a bit tedious. To save you some trouble, AWT and Swing provide some helper classes that implement these dummy methods for you. For each of the most common listener interfaces containing more than one method, there is an adapter class containing the stubbed methods. You can use the adapter class as a base class for your own adapters. When you need a class to patch together your event source and listener, you can subclass the adapter and override only the methods you want.
For example, the MouseAdapter
class implements the MouseListener
interface and provides the following minimalist implementation:
public
void
mouseClicked
(
MouseEvent
e
)
{};
public
void
mousePressed
(
MouseEvent
e
)
{};
public
void
mouseReleased
(
MouseEvent
e
)
{};
public
void
mouseEntered
(
MouseEvent
e
)
{};
public
void
mouseExited
(
MouseEvent
e
)
{};
This isn’t a tremendous time saver; it’s simply a bit of sugar.
The primary advantage comes into play when we use the MouseAdapter
as the base for our own adapter
in an anonymous inner class. For example, suppose we want to catch a
mousePressed()
event in
some component and blow up a building. We can use the following to make
the hookup:
someComponent
.
addMouseListener
(
new
MouseAdapter
()
{
public
void
MousePressed
(
MouseEvent
e
)
{
building
.
blowUp
();
}
}
);
We’ve taken artistic liberties with the formatting, but it’s pretty readable. Moreover, we’ve avoided creating stub methods for the four unused event handler methods. Writing adapters is common enough that it’s nice to avoid typing those extra few lines and perhaps stave off the onset of carpal tunnel syndrome for a few more hours. Remember that any time you use an inner class, the compiler is generating a class for you, so the messiness you’ve saved in your source still exists in the output classes.
Get Learning Java, 4th Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.