The first way is to have a class (usually your Frame, but it can be any class) implement some *Listener interface and override it's methods to listen for events. Example:
class MyFrame extends Frame implements ActionListener
{
public static void main(String[] args) {
MyFrame frame = new MyFrame();
frame.show();
}
JButton btn = new JButton("Please do not click this button");
MyFrame() {
setTitle("Implements Listener");
addWindowListener(new WindowCloser());
setSize(400, 100);
// This frame will listen for events generated by the button
// (Effectively, this line registers a callback function)
btn.addActionListener(this);
add(BorderLayout.CENTER, btn);
}
// this method is inherited from ActionListener and must be overridden
// (This is the callback function)
public void actionPerformed(ActionEvent e) {
// this code responds to the event (it is the event handler)
btn.setText("Please do not click this button again");
}
}
The alert student will note that there is a problem with the above approach: the frame can only handle events from one button. This is somewhat limiting. Thankfully, the Java designers have blessed us with the ability to distinguish between different event sources by calling setActionCommand on the event sources, and getActionCommand in the event handler. Example:
MyFrame() { // within the constructor
...
Button newBtn = new Button("New");
newBtn.addActionListener(this);
newBtn.setActionCommand("New");
...
Button saveBtn = new Button("Save");
saveBtn.addActionListener(this);
saveBtn.setActionCommand("Save");
...
}
public void actionPerformed(ActionEvent e) {
// this code distinguishes between the buttons
if (e.getActionCommand().equals("New")) {
newFile();
} else if (e.getActionCommand().equals("Save")) {
saveFile();
}
...
}
Remember to use the equals() method of the String class.
Some of the *Listener interfaces are designed to handle numerous different events. Consequently, they have a whole schload of methods, such as the WindowListener. However, sometimes you're only interested in one or two of those events. This can lead to ugly code like:
class MyFrame extends Frame implements WindowListener {
MyFrame() {
addWindowListener(this);
...
}
...
// This is the only event we are interested in
// (makes the frame closable)
public void windowClosing(WindowEvent e) {
System.exit(0);
}
// But we have to override _all_ of these because
// they're methods in the interface (what a pain)
public void windowOpened(WindowEvent e) { }
public void windowIconified(WindowEvent e) { }
public void windowDeiconified(WindowEvent e) { }
public void windowClosed(WindowEvent e) { }
public void windowActivated(WindowEvent e) { }
public void windowDeactivated(WindowEvent e) { }
}
To alleviate part of this problem, different interfaces have been made which apply to the same event type -- such as a window -- but listen for a specific category events -- such as the WindowListener, WindowFocusListener, and WindowStateListner. None of these will aleviate the above problem, though.
A better way to deal with the above problem is to subclass an Adapter an "Adapter" is simply a class that implements a similarly named *Listener interface. Examples: WindowAdapter implements WindowListener, MouseAdapter implements MouseListener, KeyAdapter implements KeyListener. (You get the idea.)
So, the above example can be simplified using an Adapter like so:
class MyFrame extends Frame {
MyFrame() {
addWindowListener(new WindowCloser());
...
}
...
}
class WindowCloser extends WindowAdapter {
// this is the only method we need to override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}
This approach is best when you think you'll want to re-use your event handling code in various different places (or even different applciations). As it turns out, this approach is also a little more rare because event-handling code tends to be pretty application-specific.
Lastly, for those occasions where you just need a quick-and-dirty event handler and you aren't going to be reusing the code elsewhere, you can just use an anonymous, inner class, like so:
class MyFrame extends Frame {
public static void main(String[] args) {
MyFrame frame = new MyFrame();
frame.show();
}
final Button btn = new Button("Click me!");
MyFrame() {
btn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
btn.setText("Thanks for the Click!");
}
});
add(BorderLayout.CENTER, btn);
}
}
Explanation:
It is also possible to simply make an inner class without making it anonymous.
| Approach | Frequency | When you should use: |
|---|---|---|
| Implement Listener | very often | to localize event handling code |
| Subclass Adapter | seldom | to re-use event handling code between different apps / containers |
| Anonymous Inner Class | often | to localize all code for a single component, or for a one-shot event |
Please note that the three ways described above are basic examples that can be combined in various different ways. Many of the example programs that follow use a hybrid approach.