Chapter 14
Responding to Keystrokes and Mouse Clicks
In This Chapter
Creating code to handle mouse clicks (and other such events)
Writing and using a Java interface
In the late 1980s, I bought my first mouse. I paid $100 and, because I didn’t really need a mouse, I checked with my wife before buying it. (At the time, my computer ran a hybrid text/windowed environment. Anything that I could do with a mouse, I could just as easily do with the Alt key.)
Now it’s the 21st century. The last ten mice that I got were free. Ordinary ones just fall into my lap somehow. A few exotic mice were on sale at the local computer superstore. One cost $10 and came with a $10 rebate.
As I write this chapter, I’m using the most recent addition to my collection — an official For Dummies mouse. This yellow and white beauty has a little compartment filled with water. Instead of a snowy Atlantic City scene, the water surrounds a tiny Dummies Man charm. It’s so cute. It was a present from the folks at Wiley Publishing.
Go On . . . Click That Button
In previous chapters, I create windows that don’t do much. A typical window displays some information but doesn’t have any interactive elements. Well, the time has come to change all that. This chapter’s first example is a window with a button on it. When the user clicks the button, darn it, something happens. The code is shown in Listing 14-1, and the main method that calls the code in Listing 14-1 is in Listing 14-2.
Listing 14-1: A Guessing Game
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
class GameFrame extends JFrame
implements ActionListener
{
private static final long serialVersionUID = 1L;
int randomNumber = new Random().nextInt(10) + 1;
int numGuesses = 0;
JTextField textField = new JTextField(5);
JButton button = new JButton(“Guess”);
JLabel label = new JLabel(numGuesses + “ guesses”);
public GameFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
add(textField);
add(button);
add(label);
button.addActionListener(this);
pack();
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e)
{
String textFieldText = textField.getText();
if (Integer.parseInt(textFieldText)==randomNumber) {
button.setEnabled(false);
textField.setText(textField.getText() + “ Yes!”);
textField.setEnabled(false);
} else {
textField.setText(“”);
textField.requestFocus();
}
numGuesses++;
String guessWord =
(numGuesses == 1) ? “ guess” : “ guesses”;
label.setText(numGuesses + guessWord);
}
}
Listing 14-2: Starting the Guessing Game
class ShowGameFrame {
public static void main(String args[]) {
new GameFrame();
}
}
Some snapshots from a run of this section’s code are shown in Figures 14-1 and 14-2. In a window, the user plays a guessing game. Behind the scenes, the program chooses a secret number (a number from 1 to 10). Then the program displays a text field and a button. The user types a number in the text field and clicks the button. One of two things happens next:
If the number that the user types in isn’t the same as the secret number, the computer posts the number of guesses made so far. The user gets to make another guess.
If the number that the user types in is the same as the secret number, the text field displays Yes!
. Meanwhile, the game is over, so both the text field and the button become disabled. Both components have that gray, washed-out look, and neither component responds to keystrokes or mouse clicks.
In Listing 14-1, the code to create the frame, the button, and the text field isn’t earth-shattering. I did similar things in Chapters 9 and 10. The JTextField
class is new in this chapter, but a text field isn’t much different from a button or a label. Like so many other components, the JTextField
class is defined in the javax.swing
package. When you create a new JTextField
instance, you can specify a number of columns. In Listing 14-1, I create a text field that’s five columns wide.
Events and event handling
The big news in Listing 14-1, shown in the preceding section, is the handling of the user’s button click. When you’re working in a graphical user interface (GUI), anything the user does (like pressing a key, moving the mouse, clicking the mouse, or whatever) is called an event. The code that responds to the user’s press, movement, or click is called event-handling code.
Listing 14-1 deals with the button-click event with three parts of its code:
The top of the GameFrame
class declaration says that this class implements ActionListener
.
The constructor for the GameFrame
class adds this
to the button’s list of action listeners.
The code for the GameFrame
class has an actionPerformed
method.
Taken together, all three of these tricks make the GameFrame
class handle button clicks. To understand how it works, you have to know about something called an interface, which I discuss in the following section.
The Java interface
You may have noticed that, in Java, you never get a class to extend more than one parent class. In other words, you never say
class
DontDoThis
extends FirstClass, SecondClass
A class can have only one parent class, and that’s fine when you want your new class to be like a frame. But what if you want your new class to be like a frame and a button-click-listening thing? Can your new class be like both things?
Yes, it can be. Java has this thing called an interface. An interface is like a class, but it’s different. (So, what else is new? A cow is like a planet, but it’s quite a bit different. Cows moo; planets hang in space.) Anyway, when you hear the word interface, you can start by thinking of a class. Then, in your head, note the following things:
A class can extend only one parent class, but a class can implement more than one interface.
For instance, if you want GameFrame
to listen for keystrokes as well as button clicks, you can say
class GameFrame extends JFrame
implements ActionListener, ItemListener
An interface’s methods have no bodies of their own.
Here’s a copy of the API code for the ActionListener
interface:
package java.awt.event;
import java.util.EventListener;
public interface ActionListener
extends EventListener {
public void actionPerformed(ActionEvent e);
}
I’ve removed the code’s comments, but I’ve avoided messing with the API code in any significant ways. In this code, the actionPerformed
method has no body — no curly braces and no statements to execute. In place of a body, there’s just a semicolon.
A method with no body, like the method defined in the ActionListener
interface, is called an abstract method.
When you implement an interface, you provide bodies for all the interface’s methods.
That’s why an actionPerformed
method appears in Listing 14-1. By announcing that it will implement the ActionListener
interface, the code in Listing 14-1 agrees that it will give meaning to the interface’s actionPerformed
method. In this situation, giving meaning means declaring an actionPerformed
method with curly braces, a body, and maybe some statements to execute.
When you announce that you’re going to implement an interface, the Java compiler takes this announcement seriously. Later on in the code, if you fail to give meaning to any of the interface’s methods, the compiler yells at you.
Threads of execution
Here’s a well-kept secret: Java programs are multithreaded, which means that several things are going on at once whenever you run a Java program. Sure, the computer is executing the code that you’ve written, but it’s executing other code as well (code that you didn’t write and don’t see). All this code is being executed at the same time. While the computer executes your main
method’s statements, one after another, the computer takes time out, sneaks away briefly, and executes statements from some other, unseen methods. For most simple Java programs, these other methods are ones that are defined as part of the Java virtual machine (JVM).
For instance, Java has an event-handling thread. While your code runs, the event-handling thread’s code runs in the background. The event-handling thread’s code listens for mouse clicks and takes appropriate action whenever a user clicks the mouse. Figure 14-3 illustrates how this works.
When the user clicks the button, the event-handling thread says, “Okay, the button was clicked. So, what should I do about that?” And the answer is, “Call some actionPerformed
methods.” It’s as if the event-handling thread has code that looks like this:
if (buttonJustGotClicked()) {
object1.actionPerformed(infoAboutTheClick);
object2.actionPerformed(infoAboutTheClick);
object3.actionPerformed(infoAboutTheClick);
}
Of course, behind every answer is yet another question. In this situation, the follow-up question is, “Where does the event-handling thread find actionPerformed
methods to call?” And there’s another question: “What if you don’t want the event-handling thread to call certain actionPerformed
methods that are lurking in your code?”
Well, that’s why you call the addActionListener
method. In Listing 14-1, the call
button.addActionListener(this);
tells the event-handling thread, “Put this code’s actionPerformed
method on your list of methods to be called. Call this code’s actionPerformed
method whenever the button is clicked.”
So, that’s how it works. To have the computer call an actionPerformed
method, you register the method with Java’s event-handling thread. You do this registration by calling addActionListener
. The addActionListener
method belongs to the object whose clicks (and other events) you’re waiting for. In Listing 14-1, you’re waiting for the button object to be clicked, and the addActionListener
method belongs to that button object.
The keyword this
In Chapters 9 and 10, the keyword this
gives you access to instance variables from the code inside a method. So, what does the this
keyword really mean? Well, compare it with the English phrase “state your name.”
I, (state your name), do solemnly swear, to uphold the constitution of the Philadelphia Central High School Photography Society. . . .
The phrase “state your name” is a placeholder. It’s a space in which each person puts his or her own name.
I, Bob, do solemnly swear. . . .
I, Fred, do solemnly swear. . . .
Think of the pledge (“I . . . do solemnly swear . . .”) as a piece of code in a Java class. In that piece of code is the placeholder phrase, “state your name.” Whenever an instance of the class (a person) executes the code (that is, takes the pledge), the instance fills in its own name in place of the phrase “state your name.”
The this
keyword works the same way. It sits inside the code that defines the GameFrame
class. Whenever an instance of GameFrame
is constructed, the instance calls addActionListener(this)
. In that call, the this
keyword stands for the instance itself.
button.addActionListener(
thisGameFrameInstance
);
By calling button.addActionListener(this)
, the GameFrame
instance is saying, “Add my actionPerformed
method to the list of methods that are called whenever the button is clicked.” And indeed, the GameFrame
instance has an actionPerformed
method. The GameFrame
has to have an actionPerformed
method because the GameFrame
class implements the ActionListener
interface. It’s funny how that all fits together.
Inside the actionPerformed method
The actionPerformed
method in Listing 14-1 uses a bunch of tricks from the Java API. Here’s a brief list of those tricks:
Every instance of JTextField
(and of JLabel
) has its own getter and setter methods, including getText
and setText
. Calling getText
fetches whatever string of characters is in the component. Calling setText
changes the characters that are in the component. In Listing 14-1, judicious use of getText
and setText
pulls a number out of the text field and replaces the number with either nothing (the empty string “”
), or the number, followed by the word Yes!
Every component in the javax.swing
package (JTextField
, JButton
, or whatever) has a setEnabled
method. When you call setEnabled(false)
, the component gets that limp, gray, washed-out look and can no longer receive button clicks or keystrokes.
Every component in the javax.swing
package has a requestFocus
method. When you call requestFocus
, the component gets the privilege of receiving the user’s next input. For example, in Listing 14-1, the call textField.requestFocus()
says “even though the user may have just clicked the button, put a cursor in the text field. That way, the user can type another guess in the text field without clicking the text field first.”
The serialVersionUID
Chapter 9 introduces the SuppressWarnings
annotation to avoid dealing with something called a serialVersionUID
. A serialVersionUID
is a number that helps Java avoid version conflicts when you send an object from one place to another. For example, you can send the state of your JFrame
object to another computer’s screen. Then the other computer can check the frame’s version number to make sure that no funny business is taking place.
In Chapter 9, I side-step the serialVersionUID
issue by telling Java to ignore any warnings about missing serial version numbers. But in Listing 14-1, I take a bolder approach. I give my JFrame
object a real serialVersionUID
. This is my first version of GameFrame
, so I give this GameFrame
the version number 1. (Actually, I give this GameFrame
the number 1L
, meaning the long
value 1. See Chapter 4.)
So when would you bother to change a class’s serialVersionUID
number? If version number 1 is nice, is version number 2 even better? The answer is complicated but the bottom line is, don’t change the serialVersionUID
number unless you make incompatible changes to the class’s code. By “incompatible changes,” I mean changes that make it impossible for the receiving computer’s existing code to handle your newly created objects.
Responding to Things Other Than Button Clicks
When you know how to respond to one kind of event, responding to other kinds of events is easy. Listings 14-3 and 14-4 display a window that converts between U.S. and U.K. currencies. The code in these listings responds to many kinds of events. Figures 14-4, 14-5, and 14-6 show some pictures of the code in action.
Listing 14-3: Displaying the Local Currency
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.text.NumberFormat;
import java.util.Locale;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
class MoneyFrame extends JFrame implements
KeyListener, ItemListener, MouseListener {
private static final long serialVersionUID = 1L;
JLabel fromCurrencyLabel = new JLabel(“ “);
JTextField textField = new JTextField(5);
JLabel label = new JLabel(“ “);
JComboBox combo = new JComboBox();
NumberFormat currencyUS =
NumberFormat.getCurrencyInstance();
NumberFormat currencyUK =
NumberFormat.getCurrencyInstance(Locale.UK);
public MoneyFrame() {
setLayout(new FlowLayout());
add(fromCurrencyLabel);
add(textField);
combo.addItem(“US to UK”);
combo.addItem(“UK to US”);
add(label);
add(combo);
textField.addKeyListener(this);
combo.addItemListener(this);
label.addMouseListener(this);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 100);
setVisible(true);
}
void setTextOnLabel() {
String amountString = “”;
String fromCurrency = “”;
try {
double amount =
Double.parseDouble(textField.getText());
if(combo.getSelectedItem().equals(“US to UK”))
{
amountString = “ = “ +
currencyUK.format(amount * 0.61214);
fromCurrency = “$”;
}
if(combo.getSelectedItem().equals(“UK to US”))
{
amountString = “ = “ +
currencyUS.format(amount * 1.63361);
fromCurrency = “u00A3”;
}
} catch (NumberFormatException e) {
}
label.setText(amountString);
fromCurrencyLabel.setText(fromCurrency);
}
@Override
public void keyReleased(KeyEvent k) {
setTextOnLabel();
}
@Override
public void keyPressed(KeyEvent k) {
}
@Override
public void keyTyped(KeyEvent k) {
}
@Override
public void itemStateChanged(ItemEvent i) {
setTextOnLabel();
}
@Override
public void mouseEntered(MouseEvent m) {
label.setForeground(Color.red);
}
@Override
public void mouseExited(MouseEvent m) {
label.setForeground(Color.black);
}
@Override
public void mouseClicked(MouseEvent m) {
}
@Override
public void mousePressed(MouseEvent m) {
}
@Override
public void mouseReleased(MouseEvent m) {
}
}
Listing 14-4: Calling the Code in Listing 14-3
class ShowMoneyFrame {
public static void main(String args[]) {
new MoneyFrame();
}
}
Okay, so Listing 14-3 is a little long. Even so, the outline of the code in Listing 14-3 isn’t too bad. Here’s what the outline looks like:
class MoneyFrame extends JFrame implements
KeyListener, ItemListener, MouseListener {
variable declarations
constructor for the MoneyFrame class
declaration of a method named setTextOnLabel
all the methods that are required because the class
implements three interfaces
}
The constructor in Listing 14-3 adds the following four components to the new MoneyFrame
window:
A label: In Figure 14-4, the label displays a dollar sign.
A text field: In Figure 14-4, the user types 54 in the text field.
Another label: In Figure 14-4, the label displays £33.06
.
A combo box: In Figure 14-4, the combo box displays US to UK. In Figure 14-5, the user selects an item in the box. In Figure 14-6, the selected item is UK to US.
The MoneyFrame
implements three interfaces — the KeyListener
, ItemListener
, and MouseListener
interfaces. Because it implements three interfaces, the code can listen for three different kinds of events. I discuss the interfaces and events in the following list:
KeyListener
: A class that implements the KeyListener
interface must have three methods named keyReleased
, keyPressed
, and keyTyped
. When you lift your finger off a key, the event-handling thread calls keyReleased
.
In Listing 14-3, the keyReleased
method calls setTextOnLabel
. My setTextOnLabel
method checks to see what’s currently selected in the combo box. If the user selects the US to UK option, the setTextOnLabel
method converts dollars to pounds. If the user selects the UK to US option, the setTextOnLabel
method converts pounds to dollars.
In the setTextOnLabel
method, I use the string “u00A3”
. The funny-looking u00A3
code is Java’s UK pound sign. (The u
in u00A3
stands for Unicode — an international standard for representing characters in the world’s alphabets.) If my operating system’s settings defaulted to UK currency, in the runs of Java programs, the pound sign would appear on its own. For information about all this, check out the Locale
class in Java’s API documentation.
By the way, if you’re thinking in terms of real currency conversion, forget about it. This program uses rates that may or may not have been accurate at one time. Sure, a program can reach out on the Internet for the most up-to-date currency rates, but at the moment, you have other Javafish to fry.
ItemListener
: A class that implements the ItemListener
interface must have an itemStateChanged
method. When you select an item in a combo box, the event-handling thread calls itemStateChanged
.
In Listing 14-3, when the user selects US to UK or UK to US in the combo box, the event-handling thread calls the itemStateChanged
method. In turn, the itemStateChanged
method calls setTextOnLabel
, and so on.
MouseListener
: A class that implements the MouseListener
interface must have mouseEntered
, mouseExited
, mouseClicked
, mousePressed
, and mouseReleased
methods. Implementing MouseListener
is different from implementing ActionListener
. When you implement ActionListener
, as in Listing 14-1, the event-handling thread responds only to mouse clicks. But with MouseListener
, the thread responds to the user pressing the mouse, releasing the mouse, and more.
In Listing 14-3, the mouseEntered
and mouseExited
methods are called whenever you move over or away from the label. How do you know that the label is involved? Just look at the code in the MoneyFrame
constructor. The label
variable’s addMouseListener
method is the one that’s called.
Look at the mouseEntered
and mouseExited
methods in Listing 14-3. When mouseEntered
or mouseExited
is called, the computer forges ahead and calls setForeground
. This setForeground
method changes the color of the label’s text.
Isn’t modern life wonderful? The Java API even has a Color
class with names like Color.red
and Color.black
.
Listing 14-3 has several methods that aren’t really used. For instance, when you implement MouseListener
, your code has to have its own mouseReleased
method. You need the mouseReleased
method not because you’re going to do anything special when the user releases the mouse button, but because you made a promise to the Java compiler and have to keep that promise.
Creating Inner Classes
Here’s big news! You can define a class inside of another class! For the user, Listing 14-5 behaves the same way as Listing 14-1. But in Listing 14-5, the GameFrame
class contains a class named MyActionListener
.
Listing 14-5: A class within a class
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
class GameFrame extends JFrame {
private static final long serialVersionUID = 1L;
int randomNumber = new Random().nextInt(10) + 1;
int numGuesses = 0;
JTextField textField = new JTextField(5);
JButton button = new JButton(“Guess”);
JLabel label = new JLabel(numGuesses + “ guesses”);
public GameFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
add(textField);
add(button);
add(label);
button.addActionListener(
new MyActionListener()
);
pack();
setVisible(true);
}
class MyActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String textFieldText =
textField
.getText();
if (Integer.parseInt
(textFieldText) == randomNumber) {
button.setEnabled(false);
textField.setText
(textField.getText() + “ Yes!”);
textField.setEnabled(false);
} else {
textField.setText(“”);
textField.requestFocus();
}
numGuesses++;
String guessWord =
(numGuesses == 1) ? “ guess” : “ guesses”;
label.setText(numGuesses + guessWord);
}
}
}
The MyActionListener
class in Listing 14-5 is an inner class. An inner class is a lot like any other class. But within an inner class’s code, you can refer to the enclosing class’s fields. For example, several statements inside MyActionListener
use the name textField
, and textField
is defined in the enclosing GameFrame
class.
Notice that the code in Listing 14-5 uses the MyActionListener
class only once. (The only use is in a call to button.addActionListener
.) So I ask, do you really need a name for something that’s used only once? No, you don’t. You can substitute the entire definition of the inner class inside the call to button.addActionListener
. When you do this, you have an anonymous inner class. Listing 14-6 shows you how it works.
Listing 14-6: A class with no name (inside a class with a name).
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
class GameFrame extends JFrame {
private static final long serialVersionUID = 1L;
int randomNumber = new Random().nextInt(10) + 1;
int numGuesses = 0;
JTextField textField = new JTextField(5);
JButton button = new JButton(“Guess”);
JLabel label = new JLabel(numGuesses + “ guesses”);
public GameFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
add(textField);
add(button);
add(label);
button.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String textFieldText = textField.getText();
if (Integer.parseInt
(textFieldText) == randomNumber) {
button.setEnabled(false);
textField.setText
(textField.getText() + “ Yes!”);
textField.setEnabled(false);
} else {
textField.setText(“”);
textField.requestFocus();
}
numGuesses++;
String guessWord =
(numGuesses == 1) ? “ guess” : “ guesses”;
label.setText(numGuesses + guessWord);
}
}
);
pack();
setVisible(true);
}
class MyActionListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
String textFieldText = textField.getText();
if (Integer.parseInt
(textFieldText) == randomNumber) {
button.setEnabled(false);
textField.setText
(textField.getText() + “ Yes!”);
textField.setEnabled(false);
} else {
textField.setText(“”);
textField.requestFocus();
}
numGuesses++;
String guessWord =
(numGuesses == 1) ? “ guess” : “ guesses”;
label.setText(numGuesses + guessWord);
}
}
}
Inner classes are good for things like event handlers, such as the actionPerformed
method in this chapter’s examples. The most difficult thing about an anonymous inner class is keeping track of the parentheses, the curly braces, and the indentation. So my humble advice is, start by writing code without any inner classes, such as the code in Listing 14-1. Later, when you become bored with ordinary Java classes, experiment by changing some of your ordinary classes into inner classes.