In the previous chapter, you find out how to create Swing frames that include panels, labels, and buttons. However, the frames don't do anything other than sit there. They look good, but they're completely unresponsive. Click them all you want, but they don't do anything. They're kind of like teenagers.
In this chapter, you get those buttons to do something. Specifically, you find out how to write code that's executed when a user clicks a button. The technique you use to do that is called event listening, and it's one of the most important aspects of writing Swing programs. It may seem a little complicated at first, but after you get the swing of it (sorry), event listening makes perfect sense.
Tip |
Although event listening is used mostly to respond to button clicks, it can also be used to respond to other types of user interactions. For example, you can use event listening to write code that's executed when the user makes a selection from a combo box, moves the mouse over a label, or presses a key on the keyboard. The event-listening techniques in this chapter work for those events as well. |
An event is an object that's generated when the user does something noteworthy with one of your user-interface components. For example, if the user clicks a button, drags the mouse over a label, or selects an item from a combo box, an event object is generated. This event object is then passed to a special method you create called an event listener. The event listener can examine the event object, determine exactly what type of event occurred, and respond accordingly. For example, if the user clicks a button, the event listener might write any data entered by the user via text fields to a file. If the user passes the mouse over a label, the event handler might change the text displayed by the label. And if the user selects an item from a combo box, the event handler might use the value that was selected to look up information in a database. The possibilities are endless!
There are several different types of event objects, represented by various classes that all inherit AWTEvent. Table 2-1 lists the most commonly used event classes. In addition, this table lists the listener interface that's used to create an object that can listen for the event and handle it when it is generated.
Event |
Class Listener Interface |
Description |
---|---|---|
ActionEvent |
ActionListener |
Created when the user performs an action with a button or other component. Usually, this means that the user has clicked the button. However, the user can also invoke a button action by tabbing to the button and pressing the Enter key. |
ItemEvent |
ItemListener |
Created when the selected item in a list control, such as a combo box or list box, is changed. |
DocumentEvent |
DocumentListener |
Created when the user changes the contents of a text component. such as a text field. |
WindowEvent |
WindowListener |
Created when the status of the window (frame) changes. |
KeyEvent |
KeyListener |
Created when the user presses a key on the keyboard. This event can be used to watch for specific keystrokes entered by the user. |
MouseEvent |
MouseListener |
Created when the user does something interesting with the mouse, such as clicks one of the buttons, drags the mouse, or simply moves the mouse over another object. |
FocusEvent |
FocusListener |
Created when a component receives or loses focus. |
TECHNICAL STAUFF |
Note that most of these event classes are contained in the package java.awt. The only thing not in java.awt is DocumentEvent (which happens to be an interface, not a class, in the package javax.swing.event). Strictly speaking, event handling is provided by AWT, not by Swing. |
TECHNICAL STAUFF |
The events listed in Table 2-1 can be divided into two categories. The first three (ActionEvent, ItemEvent, and DocumentEvent) are called semantic events because they're related to user interactions that usually have some specific meaning. For example, when the user clicks a button, he or she is trying to do something specific. In contrast, the other events are called low-level events. |
Tip |
If you're cramming for the test, you absolutely need to know three important terms:
|
Listener |
Interface Method |
Description |
---|---|---|
ActionListener (ActionEvent e) |
void actionPerf ormed |
Called when an action event occurs. |
ItemListener |
void itemStateChanged (itemEvent e) |
Called when the selected item changes. |
DocumentListener |
void changeUpdate (DocumentEvent e) |
Called when text is changed. |
void insertUpdate (DocumentEvent e) |
Called when text is inserted. |
|
void removeUpdate DocumentEvent e) |
Called when text is deleted. |
|
WindowListener |
void windowActivated (WindowEvent e) |
Called when the window is activated. |
void windowClosed (WindowEvent e) |
Called when the window has been closed. |
|
void windowClosing (WindowEvent e) |
Called when the user attempts to close the window. |
|
void windowDeactivated (WindowEvent e) |
Called when the window is deactivated. |
|
void windowDeiconif ied (WindowEvent e) |
Called when the window is changed from icon to normal. |
|
void windowlconif ied (WindowEvent e) |
Called when the window is minimized to an icon. |
|
void windowOpened (WindowEvent e) |
Called when the window is opened. |
|
KeyListener |
void keyPressed (KeyEvent e) |
Called when the user presses a key. |
void keyReleased (KeyEvent e) |
Called when the user releases a key. |
|
void keyTyped (KeyEvent e) |
Called when the user types a key. |
|
MouseListener |
void moused icked (MouseEvent e) |
Called when the user clicks the mouse. |
void mouseEntered MouseEvent e) |
Called when the mouse moves over a component. |
|
void mouseExited (MouseEvent e) |
Called when the mouse leaves a component. |
|
void mousePressed (MouseEvent e) |
Called when the user presses the mouse button. |
|
void mouseReleased (MouseEvent e) |
Called when the user releases the mouse button. |
|
FocusListener |
void focusGained (FocusEvent e) |
Called when the component gains focus. |
FocusListener |
void focusLost (FocusEvent e) |
Called when the component looses focus. |
Now that you know the basic objects that are used for event handling, you have to actually wire them up to create a program that responds to events.
To write Java code that responds to events, you have to do the following:
You want to add buttons or other components that generate events to your frame so it displays components the user can interact with. (Strictly speaking, all Swing components can generate some events.
Even a label generates a MouseEvent when the user moves the mouse over it. But a frame that consists of nothing but labels isn't very useful.)
Usually, you declare the variable that refers to the event source as a private class field, outside of the constructor for the frame or any other method. For example
private JButton button1;
Then, in the constructor for the frame class, you can create the button. For example, here's code that creates a panel, creates a button, adds it to a panel, and then adds the panel to the frame:
JPanel panel = new JPanel(); button1 = new JButton("Click me!"); panel.add(button1); this.add(panel);
Note that this code appears in the constructor of the frame class, so in the last line this refers to the frame.
For example, to handle action events, you should create a class that implements the ActionListener interface. As you see later in this chapter, you can do that in at least four ways. But one of the easiest is to simply add implements ActionListener to the definition of the frame class. Thus, the frame class declaration looks something like this:
public class ClickMe extends JFrame implements ActionListener
Later in this chapter, I tell you about other ways to implement the event listener interface.
When you implement a listener interface, you must provide an implementation of each method defined by the interface. Most listener interfaces define just one method, corresponding to the type of event the interface listens for. For example, the ActionListener interface defines a method named actionPerformed. This method is called whenever an action event is created. Thus the code you place inside the actionPerformed method is executed when an action event occurs.
For example, here's an actionPerformed method that responds to action events:
public void actionPerformed(ActionEvent e) { if (e.getSource() == button1) button1.setText("You clicked!"); }
Here this code changes the text displayed by button1 if the event source is button1.
The final step is to register the event listener with the event source. Every component that serves as an event source provides a method that lets you register event listeners to listen for the event. For ActionEvent sources, the method is addActionListener. Here's a modification to the frame constructor code that creates the button1 button and registers the frame class as the action event listener:
JPanel panel = new JPanel(); button1 = new JButton("Click me!"); button1.addActionListener(this); panel.add(button1); this.add(panel);
Here you can specify this as the event listener because the frame class itself implements ActionListener.
To see how all these elements work together in a complete program, Figure 2-1 shows three incarnations of a frame created by a program called ClickMe. This program displays a frame that has a single button that initially says Click Me! When the user clicks the button, the button's text changes to I've been clicked! (as shown in the second frame in the figure). Then, if the user clicks the button again, the text changes to I've been clicked 2 times! (as shown in the third frame). Thereafter, the count increases each time the button is clicked. Listing 2-1 shows the complete code for this program.
Figure 2-1: The ClickMe program in action.
Listing 2-1: The ClickMe Program
import javax.swing.*; import java.awt.event.*; → 2 public class ClickMe extends JFrame implements ActionListener → 5 { public static void main(String [] args) → 7 { new ClickMe(); } private JButton button1; → 12 public ClickMe() → 14 { this.setSize(200,100); this.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); this.setTitle("I'm Listening"); JPanel panel1 = new JPanel(); button1 = new JButton("Click Me!"); → 21 button1.addActionListener(this); → 22 panel1.add(button1); this.add(panel1); this.setVisible(true); } private int clickCount = 0; → 29 public void actionPerformed(ActionEvent e) → 31 { if (e.getSource() == button1) → 33 { clickCount++; → 35 if (clickCount == 1) → 36 button1.setText("I've been clicked!"); else button1.setText("I've been clicked" + clickCount + " times!"); } } }
The following paragraphs point out some key lines of the program:
→ 2 |
The program must import the java.awt.event package, which defines the ActionEvent class and the ActionListener interfaces. |
→ 5 |
The ClickMe class extends the JFrame class and implements ActionListener. That way this class does double-duty: It defines the frame, and it listens for events generated by components added to the frame. |
→ 7 |
The main method is required as usual. It simply creates an instance of the ClickMe class to get the frame started. |
→ 12 |
The button1 variable is defined as a private class field so both the ClickMe constructor and the actionPerformed method can access it. |
→ 14 |
The constructor does all the usual stuff that a constructor for a frame class does: It sets the frame size, default close operation, and title, and then it adds components to the frame. It ends by calling setVisible to make the frame visible on the screen. |
→ 21 |
This line creates a button and assigns it to the button1 field. |
→ 22 |
This line adds the current object as an action listener for the button1 button. |
→ 29 |
A field named clickCount is used to keep track of how many times the button has been clicked. This field is initialized to zero when the object is created. |
→ 31 |
The actionPerformed method must be coded because the ClickMe class implements the ActionListener interface. This method is called by the button1 object whenever the user clicks the button. The ActionEvent parameter is the event generated by the button click. |
→ 33 |
The getSource method of the ActionEvent parameter is called to determine the event source. In this program, this if statement isn't really required, because the program has only one event source. However, most Swing programs have more than one event source, so you need to test the event source in the event listener. |
→ 35 |
This statement increments the click count to indicate that the button has been clicked. |
→ 36 |
This if statement tests the value of the clickCount field and changes the text displayed by the button accordingly. |
Open table as spreadsheet
As explained in Book III, Chapter 7, an inner class is a class that's nested within another class. Inner classes are commonly used for event listeners. That way the class that defines the frame doesn't also have to implement the event listener. Instead, it includes an inner class that handles the events.
Listing 2-2 shows a version of the ClickMe program that uses an inner class to handle the action event for the button.
Listing 2-2: The ClickMe2 Program with an Inner Class
import javax.swing.*; import java.awt.event.*; public class ClickMe2 extends JFrame → 4 { public static void main(String [] args) { new ClickMe2(); } private JButton button1; → 11 public ClickMe2() { this.setSize(200,100); this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE); this.setTitle("I'm Listening"); ClickListener cl = new ClickListener(); → 20 JPanel panel1 = new JPanel(); button1 = new JButton("Click Me!"); button1.addActionListener(cl); → 24 panel1.add(button1); this.add(panel1); this.setVisible(true); } private class ClickListener implements ActionListener → 32 { private int clickCount = 0; → 34 public void actionPerformed(ActionEvent e) { if (e.getSource() == button1) → 38 { clickCount++; if (clickCount == 1) button1.setText("I've been clicked " else button1.setText("I've been clicked!"); + clickCount + " times!"); } } } }
This program works essentially the same as the program shown in Listing 2-1, so I won't review the basics. Instead, I just point out some highlights:
→ 4 |
The ClickMe 2 class still extends JFrame, but doesn't implement ActionListener. |
→ 11 |
The button that serves as the event source must be referenced by a class field so the inner class can access it. This field can be private, because inner classes have access to private members of the class that contains them. |
→ 20 |
This statement creates an instance of the ClickListener class (the inner class) and assigns it to the variable c1. |
→ 24 |
This statement adds c1 as an action listener for the button. Note that because this frame has only one button, I could just as easily have omitted line 20 and coded line 24 like this: button1.addActionListener(new ClickListener()); However, because most real-world applications have more than one event source, creating an instance of the listener class first, then reusing that object as the listener for each event source is common. |
→ 32 |
The ClickListener class is declared as an inner class by placing its declaration completely within the ClickMe2 class. The ClickListener class implements the ActionListener interface so it can handle action events. |
→ 34 |
The clickCount variable is declared as a class field in the inner class. |
→ 38 |
The button1 variable is available to the inner class here because inner classes can access private members of the class that contains them. |
Open table as spreadsheet
In Book VI, Chapter 1, you find out how to create a frame that exits the application when the user clicks the frame's Exit button. To add an Exit button to your application, you must do three things:
Usually, you actually add the Exit button to a panel that is in turn added to the frame.
You normally do that by calling System.exit(0).
Warning |
In many applications, you don't want to just blindly terminate the application. Instead, you should make sure the user has saved his or her data before ending. If not, you can either save the data automatically or require the user to save the data before allowing the program to end. |
Suppose you want to change the ClickMe2 application that is shown in Listing 2-2 so that it has an Exit button, but the Exit button won't let the user quit unless he or she has clicked the ClickMe button at least once. First, you change Line 11 to declare an exitButton class field:
private JButton button1, exitButton;
Next, you add code to the constructor to create the button, register the event listener, and add the button to the panel:
exitButton = new JButton("Exit"); exitButton.addActionListener(cl); panel1.add(exitButton);
Finally, you modify the actionPerformed method of the ClickListener class to handle the Exit button:
public void actionPerformed(ActionEvent e) { if (e.getSource() == button1) { clickCount++; if (clickCount == 1) button1.setText("I've been clicked!"); else button1.setText("I've been clicked " + clickCount + " times!"); } else if (e.getSource() == exitButton) { if (clickCount > 0) System.exit(0); else { JOptionPane.showMessageDialog( ClickMe3.this, "You must click at least once!", "Not so fast, buddy", JOptionPane.ERROR_MESSAGE); } } }
Here an if statement is used to determine which button is clicked when the actionPerformed method is called. This step is necessary because the ClickHandler class is used to handle action events for both buttons. If the event source is the Exit button, another if statement checks to see if the clickCount variable is greater than zero. If it is, System.exit(0) is called to end the application. Otherwise JOptionPane is used to display an error message, and the application is not ended.
Warning |
Unfortunately, just adding this logic to the Exit button isn't enough, because the user can bypass your Exit button by clicking the frame's Close button. What you want is for the frame's Close button to act exactly like the Exit button. To do that, you need to add a window event listener in addition to an action event listener, as I describe in the next section. |
Book VI, Chapter 1 shows you how to use the setDefaultCloseOperation method of the JFrame class to let the user quit the application by using the frame's Close button. However, if your application checks to see whether data has been saved (or other conditions have been met) before allowing the user to exit, this approach won't work. In that case, you want to set up the Close button so it works the same as your Exit button.
To do that, you need to add a listener that listens for window events to the frame. When the user clicks the Close button, the frame generates a windowClosing event which you can handle by registering a WindowListener with the frame itself. Then, in the windowClosing method of the WindowListener, you can just call the Exit button's doClick event. That triggers an action event for the Exit button as if the user clicked it. Thus the Close button is handled in exactly the same way as the Exit button.
The first step to setting up a window listener is getting rid of the default behavior that automatically exits the application when the user clicks the Close button. To do that, you must change the constant you use in the setDefaultCloseOperation from EXIT_ON_CLOSE to DO_NOTHING_ ON_CLOSE, like this:
this.setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE);
This way the default action of the JFrame class is to completely ignore the Close button. Then you can install a window listener to handle the Close button any way you want.
Next, you can set up the WindowListener to listen for window events. One way to do that is to create an inner class that implements the WindowListener interface. Unfortunately, the WindowListener interface has a lot of methods, and you must provide an implementation for each method even if you don't want to do anything for that method. Thus your WindowListener looks something like this:
private class Closer implements WindowListener { public void windowClosing(WindowEvent e) { exitButton.doClick(); } public void windowActivated(WindowEvent e) {} public void windowClosed(WindowEvent e) {} public void windowDeactivated(WindowEvent e) {} public void windowDeiconified(WindowEvent e) {} public void windowIconified(WindowEvent e) {} public void windowOpened(WindowEvent e) {} }
Finally, you register the WindowListener with the frame by calling its addWindowListener method in the frame class constructor:
this.addWindowListener(new Closer());
Tip |
If you find it annoying that you have to code all those dummy methods when you implement the WindowListener interface, you can use an adapter class instead. An adapter class is a class that implements an interface and provides dummy implementations for all the methods defined by the interface. Instead of implementing the interface, you extend the adapter class. Then you only have to provide an implementation for the method or methods you're interested in. |
Many of Java's event listener interfaces have corresponding adapter classes. They're listed in Table 2-3. Here's what the Closer class looks like if you extend WindowAdapter instead of implement WindowListener:
private class Closer extends WindowAdapter { public void windowClosing(WindowEvent e) { exitButton.doClick(); } }
Listener Interface |
Adapter Class |
---|---|
WindowListener |
WindowAdapter |
KeyListener |
KeyAdapter |
MouseListener |
MouseAdapter |
FocusListener |
FocusAdapter |
That saves some code. However, you can save even more code by skipping the Closer class altogether, and handling the window closing event with an anonymous inner class instead. Then the statement in the frame constructor that registers the window event listener looks like this:
addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { exitButton.doClick(); } });
Here the window listener is created as an anonymous inner class that extends WindowAdapter and defines the windowClosing method.
REMEMBER |
An anonymous inner class is simply a class that has no name, that is used only once in the program, and is defined right at the spot where it's used. For a rundown of the weird syntax used to create an anonymous inner class, refer to Book III, Chapter 7. |
Now that you've mulled over the various techniques for creating an Exit button and handling the Close button, Listing 2-3 presents a third version of the ClickMe program that adds all these features so you can see how they work together. This version of the program adds an Exit button, but does not allow the user to quit until he or she has clicked the ClickMe button at least once. And it treats the Close button as if the user had clicked Exit.
Listing 2-3: The ClickMe Application with an Exit Button
import javax.swing.*; import java.awt.event.*; public class ClickMe3 extends JFrame { public static void main(String [] args) { new ClickMe3(); } private JButton button1, exitButton; → 11 public ClickMe3() { this.setSize(275,100); this.setTitle("I'm Listening"); this.setDefaultCloseOperation( → 17 JFrame.DO_NOTHING_ON_CLOSE); ClickListener cl = new ClickListener(); JPanel panel1 = new JPanel(); addWindowListener(new WindowAdapter() → 24 { public void windowClosing(WindowEvent e) { exitButton.doClick(); → 28 } }); button1 = new JButton("Click Me!"); button1.addActionListener(cl); panel1.add(button1); exitButton = new JButton("Exit"); → 36 exitButton.addActionListener(cl); panel1.add(exitButton); this.add(panel1); this.setVisible(true); } private class ClickListener implements ActionListener { private int clickCount = 0; public void actionPerformed(ActionEvent e) { if (e.getSource() == button1) { clickCount++; if (clickCount == 1) button1.setText("I've been clicked!"); else button1.setText("I've been clicked " + clickCount + " times!"); } else if (e.getSource() == exitButton) →61 { if (clickCount > 0) System.exit(0); else { JOptionPane.showMessageDialog( ClickMe3.this, "You must click at least once!", "Not so fast, buddy", JOptionPane.ERROR_MESSAGE); } } } } }
The following paragraphs draw your attention to the key sections of this program:
→ 11 |
The exitButton variable is declared along with the button1 variable as a class field so it can be accessed from the inner classes. |
→ 17 |
The setDefaultCloseOperation method tells the JFrame class that no default action is taken if the user closes the window. |
→ 24 |
A window listener is installed on the frame to listen for window events. This listener is constructed from an anonymous inner class that extends the WindowAdapter class. |
→ 28 |
In the windowClosing method, the doClick method of the Exit button is called. That way the Close button is handled exactly as if the user had clicked the Exit button. |
→ 36 |
This and the next two lines create the Exit button, register its action event listener, and add it to the panel. |
→ 61 |
In the actionPerformed method of the action listener class, this if statement checks to see if the action event came from the Exit button. If so, another if statement checks to see if the user has clicked the ClickMe button at least once. If so, the application exits. Otherwise a message is displayed, and the program refuses to budge. |
Open table as spreadsheet
Book I - Java Basics
Book II - Programming Basics
Book III - Object-Oriented Programming
Book IV - Strings, Arrays, and Collections
Book V - Programming Techniques
Book VI - Swing
Book VII - Web Programming
Book VIII - Files and Databases
Book IX - Fun and Games