Tải bản đầy đủ (.pdf) (150 trang)

Ivor Horton’s Beginning Java 2, JDK 5 Edition phần 7 ppt

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (1.98 MB, 150 trang )

// Handle window events
protected void processWindowEvent(WindowEvent e) {
if (e.getID() == WindowEvent.WINDOW_CLOSING) {
dispose(); // Release resources
System.exit(0); // Exit the program
}
super.processWindowEvent(e); // Pass on the event
}
private JMenuBar menuBar = new JMenuBar(); // Window menu bar
// File menu items
private JMenuItem newItem, openItem, closeItem,
saveItem, saveAsItem, printItem;
// Element menu items
private JRadioButtonMenuItem lineItem, rectangleItem, circleItem, // Types
curveItem, textItem;
private JCheckBoxMenuItem redItem, yellowItem, // Colors
greenItem, blueItem ;
}
You add the call to enableEvents() as the last in the constructor. Note that the statement that sets
EXIT_ON_CLOSE as the close option for the window is commented out. You could delete the statement if
you want. When you compile
SketchFrame and run Sketcher again, you’ll be able to close the window as
before, and the program will shut down gracefully. However, this time it’s your method that’s doing it.
How It Works
The additional import statements make the constants defined in the AWTEvent class and the
WindowEvent class name available to your source file without the need to qualify them with the
package names. You call
enableEvents() in the constructor with WINDOW_EVENT_MASK as the argu-
ment to enable window events. This enables all the window events represented by the
WindowEvent
class. An object of this class can represent one of a number of different window events that are each iden-


tified by an event ID, which is a constant defined within the
WindowEvent class. The event IDs for the
WindowEvent class are:
Event ID Description
WINDOW_OPENED The event that occurs the first time a window is made visible.
WINDOW_CLOSING The event that occurs as a result of the close icon being selected or
Close being selected from the window’s system menu.
WINDOW_CLOSED The event that occurs when the window has been closed.
WINDOW_ACTIVATED The event that occurs when the window is activated — obtains the
focus, in other words. When another GUI component has the focus,
you could make the window obtain the focus by clicking on it, for
example.
Table continued on following page
871
Handling Events
21_568744 ch18.qxd 11/23/04 9:40 PM Page 871
Event ID Description
WINDOW_DEACTIVATED The event that occurs when the window is deactivated — loses the
focus, in other words. Clicking on another window would cause this
event, for example.
WINDOW_GAINED_FOCUS The event that occurs when the window gains the focus. This implies
that the window or one of its components will receive keyboard
events.
WINDOW_LOST_FOCUS The event that occurs when the window loses the focus. This implies
that keyboard events will not be delivered to the window or any of
its components.
WINDOW_ICONIFIED The event that occurs when the window is minimized and reduced to
an icon.
WINDOW_DEICONIFIED The event that occurs when the window is restored from an icon.
WINDOW_STATE_CHANGED The event that occurs when the window state changes — when it is

maximized or minimized, for instance.
If any of these events occur, the
processWindowEvent() method that you have added to the
SketchFrame class will be called. Your version of the method overrides the base class method that is
inherited from
java.awt.Window, and is responsible for passing the event to any listeners that have
been registered. The argument of type
WindowEvent that is passed to the method will contain the event
ID that identifies the particular event that occurred. To obtain the ID of the event, you call the
getID()
method for the event object, and compare that with the ID identifying the WINDOW_CLOSING event. If the
event is
WINDOW_CLOSING, you call the dispose() method for the window to close the window and
release the system resources it is using. You then close the application by calling the
exit() method
defined in the
System class.
The
getID() method is defined in the AWTEvent class, which is a superclass of all the low-level event
classes I have discussed, so all event objects that encapsulate low-level events have this method.
In the
SketchFrame class, the dispose() method is inherited originally from the Window class via the
base class
JFrame. This method releases all the resources for the window object, including those for all
components owned by the object. Calling the
dispose() method doesn’t affect the window object itself
in the program. It just tells the operating system that the resources used to display the window and the
components it contains on the screen are no longer required. The window object is still around together
with its components, so you could call its methods or even open it again.
Note that you call the processWindowEvent() method in the superclass if it is not

the closing event. It is very important that you do this as it allows the event to be
passed on to any listeners that have been registered for these events. If you don’t
call
processWindowEvent() for the superclass, any events that you do not handle in
your
processWindowEvent() method will be lost, because the base class method is
normally responsible for passing the event to the listeners that have been registered
to receive it.
872
Chapter 18
21_568744 ch18.qxd 11/23/04 9:40 PM Page 872
If you had not commented out the call to the setDefaultCloseOperation() method, your
processWindowEvent() method would still have been called when the close icon was clicked. In this
case you would not need to call
dispose() and exit() in the method. This would all have been taken
care of automatically after your
processWindowEvent() method had finished executing. This would be
preferable as it means there would be less code in your program, and the code to handle the default
close action is there in the
JFrame class anyway.
Enabling Other Low-level Events
The enableEvents() method is inherited from the Component class. This means that any component
can elect to handle its own events. You just call the
enableEvents() method for the component and
pass an argument defining the events you want the component to handle. If you want to enable more
than one type of event for a component, you just combine the event masks from
AWTEvent that you saw
earlier by linking them with a bitwise OR. To make the window object handle mouse events as well as
window events, you could write:
enableEvents(WINDOW_EVENT_MASK | MOUSE_EVENT_MASK);

Of course, you must now also implement the processMouseEvent() method for the SketchFrame
class. Like the processWindowEvent() method, this method is protected and has void as the return
type. It receives the event as an argument of type
MouseEvent. There are two other methods specific to
the
Window class that handle events:
Event-Handling Methods Description
processWindowFocusEvent(WindowEvent e) This method is called for any window focus
events that arise as long as such events are
enabled for the window.
processWindowStateEvent(WindowEvent e) This method is called for events arising as a
result of the window changing state.
These methods and the
processWindowEvent() method are available only for objects of type Window
or of a type that is a subclass of Window, so don’t try to enable window events on other components.
The other event-handling methods that you can override to handle component events are:
Event-Handling Methods Description
processEvent(AWTEvent e) This method is called first for any events that are
enabled for the component. If you implement this
method and fail to call the base class method, none
of the methods for specific groups of events will be
called.
processFocusEvent(FocusEvent e) This method will be called for focus events, if they
are enabled for the component.
Table continued on following page
873
Handling Events
21_568744 ch18.qxd 11/23/04 9:40 PM Page 873
Event-Handling Methods Description
processKeyEvent(KeyEvent e) This method will be called for key events, if

they are enabled for the component.
processMouseEvent(MouseEvent e) This method will be called for mouse
button events, if they are enabled for the
component.
processMouseMotionEvent(MouseEvent e) This method will be called for mouse move
and drag events, if they are enabled for the
component.
processMouseWheelEvent(MouseWheelEvent e) This method will be called for mouse wheel
rotation events, if they are enabled for the
component.
All the event-handling methods for a component are protected methods that have a return type of
void.
The default behavior implemented by these methods is to dispatch the events to any listeners registered
for the component. If you don’t call the base class method when you override these methods after your
code has executed, this behavior will be lost.
Low-Level Event Listeners
To create a class that defines an event listener, your class must implement a listener interface. All event lis-
tener interfaces extend the interface
java.util.EventListener. This interface doesn’t declare any
methods, though — it’s just used to identify an interface as being an event listener interface. It also allows
a variable of type
EventListener to be used for storing a reference to any kind of event listener object.
There is a very large number of event listener interfaces. You’ll consider just eight at this point that are
concerned with low-level events. The following sections describe these interfaces and the methods they
declare.
Although it seemed to be convenient to handle the window-closing event in the
SketchFrame class by implementing processWindowEvent(), as a general rule you
should use listeners to handle events. Using listeners is the recommended approach to
handling events in the majority of circumstances, since separating the event handling
from the object that originated the event results in a simpler code structure that is eas-

ier to understand and is less error prone. You will change the handling of the window-
closing event in the Sketcher code to use a listener a little later in this chapter.
874
Chapter 18
21_568744 ch18.qxd 11/23/04 9:40 PM Page 874
The WindowListener Interface
This interface defines methods to respond to events reflecting changes in the state of a window.
Defined Methods Description
windowOpened(WindowEvent e) Called the first time the window is opened
windowClosing(WindowEvent e) Called when the system menu Close item or the
window close icon is selected
windowClosed(WindowEvent e) Called when the window has been closed
windowActivated(WindowEvent e) Called when the window is activated — by clicking
on it, for example
windowDeactivated(WindowEvent e) Called when a window is deactivated — by click-
ing on another window, for example
windowIconified(WindowEvent e) Called when a window is minimized and reduced
to an icon
windowDeiconified(WindowEvent e) Called when a window is restored from an icon
The WindowFocusListener Interface
This interface defines methods to respond to a window gaining or losing the focus. When a window has
the focus, one of its child components can receive input from the keyboard. When it loses the focus, key-
board input via a child component of the window is not possible.
Defined Methods Description
windowGainedFocus(WindowEvent e) Called when the window gains the focus such that
the window or one of its components will receive
keyboard events.
windowLostFocus(WindowEvent e) Called when the window loses the focus. After this
event, neither the window nor any of its compo-
nents will receive keyboard events.

The WindowStateListener Interface
This interface defines a method to respond to any change in the state of a window.
Defined Method Description
windowStateChanged(WindowEvent e) Called when the window state changes — when it
is maximized or iconified, for example
875
Handling Events
21_568744 ch18.qxd 11/23/04 9:40 PM Page 875
The MouseListener Interface
This interface defines methods to respond to events arising when the mouse cursor is moved into or out
of the area occupied by a component, or one of the mouse buttons is pressed, released, or clicked.
Defined Methods Description
mouseClicked(MouseEvent e) Called when a mouse button is clicked on a component—
that is, when the button is pressed and released
mousePressed(MouseEvent e) Called when a mouse button is pressed on a component
mouseReleased(MouseEvent e) Called when a mouse button is released on a component
mouseEntered(MouseEvent e) Called when the mouse enters the area occupied by a
component
mouseExited(MouseEvent e) Called when the mouse exits the area occupied by a
component
The MouseMotionListener Interface
This interface defines methods that are called when the mouse is moved or dragged with a button
pressed.
Defined Methods Description
mouseMoved(MouseEvent e) Called when the mouse is moved within a component
mouseDragged(MouseEvent e) Called when the mouse is moved within a component while
a mouse button is held down
The MouseWheelListener Interface
This interface defines a method to respond to the mouse wheel being rotated. This is frequently used to
scroll information that is displayed, but you can use it in any way that you want.

Defined Method Description
mouseWheelMoved(MouseWheelEvent e) Called when the mouse wheel is rotated
The KeyListener Interface
This interface defines methods to respond to events arising when a key on the keyboard is pressed or
released.
876
Chapter 18
21_568744 ch18.qxd 11/23/04 9:40 PM Page 876
Defined Methods Description
keyTyped(KeyEvent e) Called when a key on the keyboard is pressed and then released
keyPressed(KeyEvent e) Called when a key on the keyboard is pressed
keyReleased(KeyEvent e) Called when a key on the keyboard is released
The FocusListener Interface
This interface defines methods to respond to a component gaining or losing the focus. You might imple-
ment these methods to change the appearance of the component to reflect whether or not it has the focus.
Defined Methods Description
focusGained(FocusEvent e) Called when a component gains the keyboard focus
focusLost(FocusEvent e) Called when a component loses the keyboard focus
There is a further listener interface,
MouseInputListener, that is defined in the javax.swing.event
package. This listener implements both the MouseListener and MouseMotionListener interfaces so it
declares methods for all possible mouse events in a single interface.
The
WindowListener, WindowFocusListener, and WindowStateListener interfaces define methods
corresponding to each of the event IDs defined in the
WindowEvent class that you saw earlier. If you
deduced from this that the methods in the other listener interfaces correspond to event IDs for the other
event classes, well, you’re right. All the IDs for mouse events are defined in the
MouseEvent class.
These are:

MOUSE_CLICKED MOUSE_PRESSED MOUSE_DRAGGED
MOUSE_ENTERED MOUSE_EXITED MOUSE_RELEASED
MOUSE_MOVED MOUSE_WHEEL
The MOUSE_MOVED event corresponds to just moving the mouse. The MOUSE_DRAGGED event arises when
you move the mouse while keeping a button pressed.
The event IDs that the
KeyEvent class defines are:
KEY_TYPED KEY_PRESSED KEY_RELEASED
Those defined in the FocusEvent class are:
FOCUS_GAINED FOCUS_LOST
877
Handling Events
21_568744 ch18.qxd 11/23/04 9:40 PM Page 877
public void windowIconified(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
private SketchFrame window; // The application window
private static Sketcher theApp; // The application object
}
If you run the Sketcher program again, you will see it works just as before, but now the Sketcher class
object is handling the close operation.
How It Works
You have added import statements for the WindowEvent and WindowListener class names. The
Sketcher class now implements the WindowListener interface, so an object of type Sketcher can
handle window events. The
main() method now creates a Sketcher object and stores the reference
in the static class member
theApp.
The

createGUI() method is now an instance method, and this executes on the event-dispatching
thread as shown in the previous example. The
createGUI() method creates the application window
object as before, but now the reference is stored in a field belonging to
theApp. After setting up the win-
dow components, the
createGUI() method calls the addWindowListener() method for the window
object. The argument to the
addWindowListener() method is a reference to the listener object that is
to receive window events. Here it is the variable
this, which refers to the application object, theApp.
If you had other listener objects that you wanted to register to receive this event, you would just need
to add more calls to the
addWindowListener() method — one call for each listener.
When you implement the
WindowListener interface in the Sketcher class, you must implement all seven
methods that are declared in the interface. If you failed to do this, the class would be abstract and you
could not create an object of type
Sketcher. Only the windowClosing() method contains code here—
the bodies of all the other methods are empty because you don’t need to use them. The
windowClosing()
method does the same as the processWindowEvent() method that you implemented for the previous
version of the
SketchFrame class, but here you don’t need to check the object passed to it because the
windowClosing() method is called only for a WINDOW_CLOSING event. You don’t need to pass the event
on either; this is necessary only when you handle events in the manner I discussed earlier. Here, if there
were other listeners around for the window events, they would automatically receive the event.
You have included the code that calls
dispose() and exit() here, but if you set the default close oper-
ation in

SketchFrame to EXIT_ON_CLOSE, you could omit these, too. You really need to put your appli-
cation cleanup code only in the
windowClosing() method, and this will typically display a dialog to
just prompt the user to save any application data. You will get to that eventually.
Having to implement six methods that you don’t need is rather tedious. But you have a way to get around
this — by using what is called an adapter class, to define a listener.
Using Adapter Classes
An adapter class is a term for a class that implements a listener interface with methods that have no con-
tent, so they do nothing. The idea of this is to enable you to derive your own listener class from any of the
879
Handling Events
21_568744 ch18.qxd 11/23/04 9:40 PM Page 879
To implement a listener for a particular event type, you just need to implement the methods declared in
the corresponding interface. You could handle some of the window events for the
SketchFrame class by
making the application class the listener for window events.
Try It Out Implementing a Low-Level Event Listener
First, delete the call to the enableEvents() method in the SketchFrame() constructor. Then delete the
definition of the
processWindowEvent() method from the class definition.
Now you can modify the previous version of the
Sketcher class so that it is a listener for window events:
// Sketching application
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.WindowListener;
import java.awt.event.WindowEvent;
public class Sketcher implements WindowListener {
public static void main(String[] args) {
theApp = new Sketcher(); // Create the application object

SwingUtilities.invokeLater(
new Runnable() { // Anonymous Runnable class object
public void run() { // Run method executed in thread
theApp.creatGUI(); // Call static GUI creator
}
} );
}
// Method to create the application GUI
private void creatGUI() {
window = new SketchFrame(“Sketcher”); // Create the app window
Toolkit theKit = window.getToolkit(); // Get the window toolkit
Dimension wndSize = theKit.getScreenSize(); // Get screen size
// Set the position to screen center & size to half screen size
window.setBounds(wndSize.width/4, wndSize.height/4, // Position
wndSize.width/2, wndSize.height/2); // Size
window.addWindowListener(this); // theApp as window listener
window.setVisible(true);
}
// Handler for window closing event
public void windowClosing(WindowEvent e) {
window.dispose(); // Release the window resources
System.exit(0); // End the application
}
// Listener interface functions you must implement – but don’t need
public void windowOpened(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
878
Chapter 18
21_568744 ch18.qxd 11/23/04 9:40 PM Page 878
adapter classes that are provided, and then implement just the methods that you are interested in. The

other empty methods will be inherited from the adapter class so you don’t have to worry about them.
There’s an adapter class defined in the
javax.swing.event package that defines the methods for the
MouseInputListener interface. There are five further adapter classes defined in the java.awt.event
package that cover the methods in the other low-level listener interfaces you have seen:
FocusAdapter WindowAdapter KeyAdapter
MouseAdapter MouseMotionAdapter MouseInputAdapter
The WindowAdapter class implements all of the methods defined in the WindowListener,
WindowFocusListener, and WindowStateListener interfaces. The other five each implement
the methods in the corresponding listener interface.
To handle the window closing event for the
Sketcher application, you could derive your own class
from the
WindowAdapter class and just implement the windowClosing() method. If you also make it
an inner class for the
Sketcher class, it will automatically have access to the members of the Sketcher
object, regardless of their access specifiers. Let’s change the structure of the Sketcher class once more
to make use of an adapter class.
Try It Out Implementing an Adapter Class
The version of the Sketcher class to implement this will be as follows, with changes to the previous
version highlighted:
// Sketching application
import java.awt.Toolkit;
import java.awt.Dimension;
import javax.swing.SwingUtilities;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
public class Sketcher {
public static void main(String[] args) {
theApp = new Sketcher(); // Create the application object

SwingUtilities.invokeLater(
new Runnable() { // Anonymous Runnable class object
public void run() { // Run method executed in thread
theApp.creatGUI(); // Call static GUI creator
}
} );
}
// Method to create the application GUI
private void creatGUI() {
window = new SketchFrame(“Sketcher”); // Create the app window
Toolkit theKit = window.getToolkit(); // Get the window toolkit
Dimension wndSize = theKit.getScreenSize(); // Get screen size
880
Chapter 18
21_568744 ch18.qxd 11/23/04 9:40 PM Page 880
// Set the position to screen center & size to half screen size
window.setBounds(wndSize.width/4, wndSize.height/4, // Position
wndSize.width/2, wndSize.height/2); // Size
window.addWindowListener(new WindowHandler()); // Add window listener
window.setVisible(true); // Display the window
}
// Handler class for window events
class WindowHandler extends WindowAdapter {
// Handler for window closing event
public void windowClosing(WindowEvent e) {
window.dispose(); // Release the window resources
System.exit(0); // End the application
}
}
private SketchFrame window; // The application window

private static Sketcher theApp; // The application object
}
This example will display the same application window as the previous example.
How It Works
As the Sketcher class is no longer the listener for window, it doesn’t need to implement the
WindowListener interface. The WindowHandler class is the listener class for window events. Because
the
WindowHandler class is an inner class to the Sketcher class, it has access to all the members of the
class, so calling the
dispose() method for the window object is still quite straightforward — you just
access the
window field of the top-level class.
The
WindowAdapter object that is the listener for the window object is created in the argument to the
addWindowListener() method for window. You don’t need an explicit variable to contain it because it
will be stored in a data member of the
Window class object. This data member is inherited from the
Window superclass of the SketchFrame class.
You haven’t finished with low-level events yet by any means, and you’ll return to handling more low-
level events in the next chapter when you begin to add drawing code to the Sketcher program. In the
meantime, let’s start looking at how you can manage semantic events.
An easy mistake to make when you’re using adapter classes is to misspell the name
of the method that you are using to implement the event — typically by using the
wrong case for a letter. In this case, you won’t be overriding the adapter class method
at all; you’ll be adding a new method. Your code will compile perfectly well but your
program will not handle any events. They will all be passed to the method in the
adapter class with the name your method should have had — which does nothing, of
course. This can be a bit mystifying until you realize where the problem is.
881
Handling Events

21_568744 ch18.qxd 11/23/04 9:40 PM Page 881
Semantic Events
As you saw earlier, semantic events relate to operations on the components in the GUI for your program.
If you select a menu item or click a button, for example, a semantic event is generated. Three classes rep-
resent the basic semantic events you’ll be dealing with most of the time, and they are derived from the
AWTEvent class, as shown in Figure 18-4.
Figure 18-4
An
ActionEvent is generated when you perform an action on a component such as clicking on a
menu item or a button. An
ItemEvent occurs when a component is selected or deselected, and an
AdjustmentEvent is produced when an adjustable object, such as a scrollbar, is adjusted.
Different kinds of components can produce different kinds of semantic events. The components that can
originate these events are:
Event Type Produced by Objects of Type
ActionEvent Buttons:
JButton, JToggleButton, JcheckBox
Menus:
JMenuItem, JMenu, JCheckBoxMenuItem, JradioButtonMenuItem
Text:
JTextField
EventObject
AWTEvent
In package java.util
In package java.awt
The base class for all semantic
events. There are semantic events
specific to Swing components
derived directly from this class.
ItemEvent

These three sematic event classes are in java.awt.event
AdjustmentEventActionEvent
882
Chapter 18
21_568744 ch18.qxd 11/23/04 9:40 PM Page 882
Event Type Produced by Objects of Type
ItemEvent Buttons:
JButton, JToggleButton, JcheckBox
Menus:
JMenuItem, JMenu, JCheckBoxMenuItem, JRadioButtonMenuItem
AdjustmentEvent JScrollbar
These three types of event are also generated by the old AWT components, but I won’t go into these here
as you are concentrating on the Swing components. Of course, any class you derive from these compo-
nent classes to define your own customized components can be the source of the event that the base
class generates. If you define your own class for buttons —
MyFancyButton, say — your class will have
JButton as a base class and inherit all of the methods from the JButton class, and objects of your class
will originate events of type
ActionEvent and ItemEvent.
Quite a large number of semantic events are specific to Swing components. Classes that have
AbstractButton as a base, which includes menu items and buttons, can generate events of type
ChangeEvent that signal some change in the state of a component. Components corresponding to the
JMenuItem class and classes derived from JMenuItem can generate events of type MenuDragMouseEvent
and of type MenuKeyEvent. An AncestorEvent is an event that is communicated to a child component
from a parent component. You’ll look at the details of some of these additional events when you need to
handle them for the components in question.
As with low-level events, the most convenient way to handle semantic events is to use listeners, so I’ll
delve into the listener interfaces for semantic events next.
Semantic Event Listeners
You have a listener interface defined for each of the three semantic event types that I have introduced so

far, and they each declare a single method:
Listener Interface Method
ActionListener void actionPerformed(ActionEvent e)
ItemListener void itemStateChanged(ItemEvent e)
AdjustmentListener void adjustmentValueChanged(AdjustmentEvent e)
Since each of these semantic event listener interfaces declares only one method, there’s no need for corre-
sponding adapter classes. The adapter classes for the low-level events were there only because of the
number of methods involved in each listener interface. To define your semantic event listener objects, you
just define a class that implements the appropriate listener interface. You can try that out by implementing
a simple applet now, and then see how you can deal with semantic events in a more complicated context
by adding to the Sketcher program later.
883
Handling Events
21_568744 ch18.qxd 11/23/04 9:40 PM Page 883
import java.util.Random; // For random number generator
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class Lottery extends JApplet {
// Initialize the applet
public void init() {
// Create interface components on event dispatch thread
}
// Create User Interface for applet
public void createGUI() {
// Set up the lucky numbers buttons
// Set up the control buttons
}
// Class defining custom buttons showing lottery selection
// Each button listens for its own events
class Selection extends JButton implements ActionListener {

// Constructor
public Selection(int value) {
// Create the button showing the value
}
// Handle selection button event
public void actionPerformed(ActionEvent e) {
// Change the current selection value to a new selection value
}
// Details of the rest of the selection class definition
}
// Class defining a handler for a control button
class HandleControlButton implements ActionListener {
// Constructor
// Handle button click
public void actionPerformed(ActionEvent e) {
// Handle button click for a particular button
}
// Rest of the inner class definition
}
final static int numberCount = 6; // Number of lucky numbers
final static int minValue = 1; // Minimum in range
final static int maxValue = 49; // Maximum in range
static int[] values = new int[maxValue-minValue+1]; // Array of possible values
static { // Initialize array
for(int i = 0 ; i<values.length ; i++)
values[i] = i + minValue;
}
885
Handling Events
21_568744 ch18.qxd 11/23/04 9:40 PM Page 885

Semantic Event Handling in Applets
Event handling in an applet is exactly the same as in an application, but you ought to see it for yourself.
Let’s see how you might handle events for buttons in an applet. You can create an applet that uses some
buttons that have listeners. To make this example a bit more gripping, I’ll throw in the possibility of
monetary gain. That’s interesting to almost everybody.
Let’s suppose you want to implement an applet that will create a set of random numbers for a lottery
entry. The requirement is to generate six different random numbers between 1 and 49. It would also be
nice to be able to change a single number if you don’t like it, so you’ll add that capability as well. Since
the local lottery may not be like this, you’ll implement the applet to make it easily adaptable to fit local
requirements.
By displaying the six selected numbers on buttons, you can provide for changing one of the choices by
processing the action event for that button. Thus, clicking a button will provide another number. You’ll
also add a couple of control buttons, one to make a new selection for a complete set of lottery numbers,
and another just for fun to change the button color. Figure 18-5 shows how the applet will look when
running under
appletviewer:
Figure 18-5
Try It Out A Lottery Applet
You can outline the broad structure of the applet’s code as follows:
// Applet to generate lottery entries
import javax.swing.JButton;
import javax.swing.JApplet;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import javax.swing.SwingUtilities;
import java.awt.GridLayout;
import java.awt.FlowLayout;
import java.awt.Dimension;
import java.awt.Container;
import java.awt.Color;

884
Chapter 18
21_568744 ch18.qxd 11/23/04 9:40 PM Page 884
// An array of custom buttons for the selected numbers
private Selection[] luckyNumbers = new Selection[numberCount];
private static Random choice = new Random(); // Random number generator
}
How It Works
The applet class is called Lottery, and it contains two inner classes, Selection and
HandleControlButton. The Selection class provides a custom button that will show a number
as its label, the number being passed to the constructor as an argument. You can make an object of
the
Selection class listen for its own action events. As I said at the outset, an event for a selection
button will change the label of the button to a different value, so of course, you’ll need to make sure
this doesn’t duplicate any of the values for the other buttons.
The two control buttons will use separate listeners to handle their action events, and the response to an
event will be quite different for each of them. One control button will create a new set of lucky numbers
while the other control button will just change the color of the buttons.
The
numberCount member of the Lottery class sets the number of values that is created. The minValue
and maxValue members specify the range of possible values that lottery numbers can have. The possible
values for selections are stored in the
values array, and this is set up in the static initialization block. The
Lottery class has an array of Selection objects as a data member — you can have arrays of components
just like arrays of any other kind of object. Since the
Selection buttons will all be the same, it’s very con-
venient to create them as an array, and having an array of components enables you to set them up in a
loop. You also have a
Random object as a class member that you’ll use to generate random integers.
You can now set about filling in the sections of the program that you have roughed out.

Filling in the Details
The generation of maxCount random values from the elements in the values array is quite independent
of everything else here, so you can define a static method in the
Lottery class to do this:
public class Lottery extends JApplet {
// Generate numberCount random selections from the values array
static int[] getNumbers() {
int[] numbers = new int[numberCount]; // Store for the numbers to be returned
int candidate = 0; // Stores a candidate selection
for(int i = 0; i < numberCount; i++) { // Loop to find the selections
search:
// Loop to find a new selection different from any found so far
for(;;) {
candidate = values[choice.nextInt(values.length)];
for(int j = 0 ; j<i ; j++) { // Check against existing selections
if(candidate==numbers[j]) { // If it is the same
continue search; // get another random selection
}
}
886
Chapter 18
21_568744 ch18.qxd 11/23/04 9:40 PM Page 886
numbers[i] = candidate; // Store the selection in numbers array
break; // and go to find the next
}
}
return numbers; // Return the selections
}
// Plus the rest of the class definition
}

The getNumbers() method returns a reference to an array of values of type int that represent the
selections — which must all be different, of course. You start the process by creating an array to hold the
selections, and a variable,
candidate, to hold a potential selection for the values array. You generate a
new selection for each iteration of the outer
for loop. The process for finding an acceptable selection is
quite simple. In the indefinite
for loop with the label search, you choose a random value from the
values array using the random number generator and then check its value against any selections already
stored in the
numbers array. If it is the same as any of them, the labeled continue statement will go to the
next iteration of the indefinite
for loop. This will continue until a selection is found that is different from
the others. In this way you ensure that you end up with a set of selections that are all different.
Let’s implement the
init() method and the creatGUI() method for the Lottery class next, as these
set up the
Selection buttons and the rest of the applet.
Try It Out Setting Up the Lucky Number Buttons
The init() method has to execute only the createGUI() method on the event-dispatching thread:
// Initialize the applet
public void init() {
SwingUtilities.invokeLater( // Create interface components
new Runnable() { // on the event dispatching thread
public void run() {
createGUI();
}
} );
}
In the class outline, you identified two tasks for the createGUI() method. The first was setting up the

lucky number buttons to be contained in the
luckyNumbers array.
Here’s the code to do that:
// Create User Interface for applet
public void creatGUI() {
// Set up the selection buttons
Container content = getContentPane();
content.setLayout(new GridLayout(0,1)); // Set the layout for the applet
// Set up the panel to hold the lucky number buttons
JPanel buttonPane = new JPanel(); // Add the pane containing numbers
887
Handling Events
21_568744 ch18.qxd 11/23/04 9:40 PM Page 887
Try It Out Setting Up the Control Buttons
The listeners for each of the control buttons will be of the same class type, so the listener object will need
some way to determine which button originated a particular event. One way to do this is to use con-
stants as IDs to identify the control buttons and pass the appropriate ID to the class constructor for the
listener object.
You can define the constants
PICK_LUCKY_NUMBERS and COLOR as fields in the Lottery class for this
purpose. The
COLOR control button will also reference a couple of variables of type Color, startColor,
and
flipColor. You can add the following statements to the Lottery class after the definition of the
luckyNumbers array:
// An array of custom buttons for the selected numbers
private Selection[] luckyNumbers = new Selection[numberCount];
final public static int PICK_LUCKY_NUMBERS = 1; // Select button ID
final public static int COLOR = 2; // Color button ID
// swap colors

Color flipColor = new Color(Color.YELLOW.getRGB()^Color.RED.getRGB());
Color startColor = Color.YELLOW; // start color
The startColor field is initialized with the YELLOW color from the Color class. The flipColor field is
set to the color that results from ORing the colors
YELLOW and RED. Of course, to get a sensible color as a
result you must OR the RGB values that you obtain from the
Color objects, not the references to the
Color objects! You’ll be using the flipColor field to change the color of the buttons.
The code to add the other panel and the control buttons is as follows:
// Create User Interface for applet
public void createGUI() {
// Setting up the selections buttons as previously
// Add the pane containing control buttons
JPanel controlPane = new JPanel(new FlowLayout(FlowLayout.CENTER, 5, 10));
// Add the two control buttons
JButton button; // A button variable
Dimension buttonSize = new Dimension(100,20); // Button size
controlPane.add(button = new JButton(“Lucky Numbers!”));
button.setBorder(BorderFactory.createRaisedBevelBorder());
button.addActionListener(new HandleControlButton(PICK_LUCKY_NUMBERS));
button.setPreferredSize(buttonSize);
controlPane.add(button = new JButton(“Color”));
button.setBorder(BorderFactory.createRaisedBevelBorder());
button.addActionListener(new HandleControlButton(COLOR));
button.setPreferredSize(buttonSize);
content.add(controlPane);
}
889
Handling Events
21_568744 ch18.qxd 11/23/04 9:40 PM Page 889

// Let’s have a fancy panel border
buttonPane.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(Color.cyan,
Color.blue),
“Every One a Winner!”));
int[] choices = getNumbers(); // Get initial set of numbers
for(int i = 0; i<numberCount; i++) {
luckyNumbers[i] = new Selection(choices[i]);
buttonPane.add(luckyNumbers[i]);
}
content.add(buttonPane);
// Set up the control buttons
}
How It Works
The init() method uses the invokeLater() method from the SwingUtilities class to execute the
createGUI() method on the event-dispatching thread. This guarantees that there is no possibility of a
deadlock arising in the GUI construction process. This is the same technique that you used in the previ-
ous example.
The first step in the
createGUI() method is to define the layout manager for the applet. To make the
layout easier, you’ll use one panel to hold the selection buttons and another to hold the control buttons.
You position these panels one above the other by specifying the layout manager for the content pane of
the applet as a grid layout with one column. The top panel will contain the lucky number buttons and
the bottom panel will contain the control buttons.
The
buttonPane panel that holds the lucky number buttons is of type JPanel, so it has a FlowLayout
object as its layout manager by default. A flow layout manager allows components to assume their
“natural” or “preferred size,” so you’ll set the preferred size for the buttons in the
Selection class con-
structor. You decorate the panel with a border by calling its

setBorder() method. The argument is the
reference that is returned by the static
createTitledBorder() method from the BorderFactory class.
The first argument passed to
createTitledBorder() is the border to be used, and the second argu-
ment is the title.
You use an etched border that is returned by another static method in the
BorderFactory class. The
two arguments to this method are the highlight and shadow colors to be used for the border. A big
advantage of using the
BorderFactory methods rather than creating border objects from the border
class constructors directly is that border objects will be shared where possible, so you can use a particu-
lar border in various places in your code and only one object will be created.
The buttons to display the chosen numbers will be of type
Selection, and you’ll get to the detail of this
inner class in a moment. You call the static
getNumbers() method to obtain the first set of random val-
ues for the buttons. You then create and store each button in the
luckyNumbers array and add it to the
panel in the
for loop. Since these buttons are going to listen for their own events, you don’t need to
worry about setting separate action listeners for them. The last step here is to add the
buttonPane panel
to the content pane for the applet.
You can now add the code for the control buttons to the
createGUI() method.
888
Chapter 18
21_568744 ch18.qxd 11/23/04 9:40 PM Page 888
How It Works

You create another JPanel object to hold the control buttons and just to show that you can, you pass a
layout manager object to the constructor. It’s a
FlowLayout manager again, but this time you explicitly
specify that the components are to be centered and the horizontal and vertical gaps are to be 5 and 10
pixels, respectively.
You declare the
button variable to use as a temporary store for the reference to each button while you
set it up. You also define a
Dimension object that you’ll use to set a common preferred size for the but-
tons. The buttons in this case are
JButton components, not custom components, so you must set each of
them up here with a listener and a border. You add a raised bevel border to each button to make them
look like buttons — again using a
BorderFactory method.
The listener for each button is an object of the inner class
HandleControlButton, and you pass the
appropriate button ID to the constructor for reasons that will be apparent when you define that class.
To set the preferred size for each button object, you call its
setPreferredSize() method. The argu-
ment is a
Dimension object that specifies the width and height. Finally, after adding the two buttons to
controlPane, you add that to the content pane for the applet.
The inner class
HandleControlButton defines the listener object for each control button, so let’s imple-
ment that next.
Try It Out Defining the Control Button Handler Class
You have already determined that the HandleControlButton class constructor will accept an argument
that identifies the particular button for which it is listening. This is to enable the
actionPerformed()
method in the listener class to choose the course of action appropriate to the button. Here’s the inner

class definition to do that:
class HandleControlButton implements ActionListener {
// Constructor
public HandleControlButton(int buttonID) {
this.buttonID = buttonID; // Store the button ID
}
// Handle button click
public void actionPerformed(ActionEvent e) {
switch(buttonID) {
case PICK_LUCKY_NUMBERS:
int[] numbers = getNumbers(); // Get maxCount random numbers
for(int i = 0; i < numberCount; i++) {
luckyNumbers[i].setValue(numbers[i]); // Set the button values
}
break;
case COLOR:
Color color = new Color(
flipColor.getRGB()^luckyNumbers[0].getBackground().getRGB());
for(int i = 0; i < numberCount; i++)
luckyNumbers[i].setBackground(color); // Set the button colors
break;
}
}
890
Chapter 18
21_568744 ch18.qxd 11/23/04 9:40 PM Page 890
private int buttonID;
}
How It Works
The constructor stores its argument value in the buttonID field so each listener object will have the ID

for the button available. The
actionPerformed() method uses the button ID to select the appropriate
code to execute for a particular button. Each case in the
switch statement corresponds to a different but-
ton. You could extend this to enable the class to handle as many different buttons as you want by adding
case statements. Because of the way you have implemented the method, each button must have a unique
ID associated with it. Of course, this isn’t the only way to do this, as you’ll see in a moment.
For the
PICK_LUCKY_NUMBERS button event, you just call the getNumbers() method to produce a set of
numbers, and then call the
setValue() method for each selection button and pass a number to it. You’ll
implement the
setValue() method when you define the Selection class in detail, in a moment.
For the
COLOR button event, you create a new color by exclusive ORing (that is, XOR) the RGB value of
flipColor with the current button color. You’ll recall from the discussion of the ^ operator (in Chapter 2)
that you can use it to exchange two values, and that is what you are doing here. You defined
flipColor
as the result of exclusive ORing the two colors, Color.YELLOW and Color.RED, together. Exclusive
ORing
flipColor with either color will produce the other color, so you flip from one color to the other
automatically for each button by exclusive ORing the background and
flipColor. As I said earlier, you
must get the RGB value for each color and operate on those — you can’t apply the
^ operator to the object
references. You then turn the resulting RGB value back into a
Color object.
Let’s now add the inner class,
Selection, which defines the lucky number buttons.
Try It Out Defining the Selection Buttons

Each button will need to store the value shown on the label, so the class will need a data member for this
purpose. The class will also need a constructor, a
setValue() method to set the value for the button to a
new value, and a method to compare the current value for a button to a given value. You need to be able
to set the value for a button for two reasons—you’ll need the capability when you set up all six selec-
tions in the listener for the control button, and you’ll want to reset the value for a button to change it
individually.
The method to compare the value set for a button to a given integer will enable you to exclude a number
that was already assigned to a button when you are generating a new set of button values. You’ll also
need to implement the
actionPerformed() method to handle the action events for the button, as the
buttons are going to handle their own events. Here’s the basic code for the class definition:
class Selection extends JButton implements ActionListener {
// Constructor
public Selection(int value) {
super(Integer.toString(value)); // Call base constructor and set the label
this.value = value; // Save the value
setBackground(startColor);
setBorder(BorderFactory.createRaisedBevelBorder()); // Add button border
setPreferredSize(new Dimension(80,20));
addActionListener(this); // Button listens for itself
}
891
Handling Events
21_568744 ch18.qxd 11/23/04 9:40 PM Page 891
// Handle selection button event
public void actionPerformed(ActionEvent e) {
// Change this selection to a new selection
int candidate = 0;
for(;;) { // Loop to find a different selection

candidate = values[choice.nextInt(values.length)];
if(isCurrentSelection(candidate)) { // If it is not different
continue; // find another
}
setValue(candidate); // We have one so set the button value
return;
}
}
// Set the value for the selection
public void setValue(int value) {
setText(Integer.toString(value)); // Set value as the button label
this.value = value; // Save the value
}
// Check the value for the selection
boolean hasValue(int possible) {
return value==possible; // Return true if equals current value
}
// Check the current choices
boolean isCurrentSelection(int possible) {
for(int i = 0; i < numberCount; i++) { // For each button
if(luckyNumbers[i].hasValue(possible)) { // check against possible
return true; // Return true for any =
}
}
return false; // Otherwise return false
}
private int value; // Value for the selection button
}
How It Works
The constructor calls the base class constructor to set the initial label for the button. It also stores the

value of type
int that is passed as an argument. The setValue() method just updates the value for a
selection button with the value passed as an argument and changes the button label by calling the
setText() method, which is inherited from the base class, JButton. The hasValue() method returns
true if the argument value passed to it is equal to the current value stored in the data member value,
and
false otherwise.
The
actionPerformed() method has a little more meat to it, but the technique is similar to that in the
getNumbers() method. To change the selection, you must create a new random value for the button from
the numbers
values array, but excluding all the numbers currently assigned to the six buttons. To do this
you just check each candidate against the six existing selections by calling the
isCurrentSelection()
method and continue choosing a new candidate until you find one that’s different.
892
Chapter 18
21_568744 ch18.qxd 11/23/04 9:40 PM Page 892
In the isCurrentSelection() method, you just work through the array of Selection objects,
luckyNumbers, comparing each value with the possible argument using the hasValue() method.
If any button has the same value as
possible, the method returns true; otherwise, it returns false.
You’re ready to start generating lottery entries. If you compile the
Lottery.java file, you can run the
applet using
appletviewer. You will need an HTML file, of course. The following contents for the file
will do the job:
<APPLET CODE=”Lottery.class” WIDTH=300 HEIGHT=200>
</APPLET>
You can adjust the width and height values to suit your monitor resolution if necessary.

The applet should produce a selection each time you click the left control button. Clicking any of the
selection buttons will generate an action event that causes a new value to be created for the button. This
enables you to replace any selection that you know to be unlucky with an alternative.
Undoubtedly, anyone who profits from using this applet will have immense feelings of gratitude and
indebtedness towards the author, who will not be offended in the slightest by any offers of a portion of
that success, however large!
Alternative Event-Handling Approaches
As I indicated in the discussion, there are various approaches to implementing listeners. Let’s look at a
couple of other ways in which you could have dealt with the control button events.
Instead of passing a constant to the listener class constructor to identify which button was selected, you
could have exploited the fact that the event object has a method,
getSource(), that returns a reference
to the object that is the source of the event. To make use of this, a reference to both button objects would
need to be available to the
actionPerformed() method. You could easily arrange for this to be the case
by adding a couple of fields to the
Lottery class:
JButton pickButton = new JButton(“Lucky Numbers!”);
JButton colorButton = new JButton(“Color”);
The inner class could then be defined as follows:
class HandleControlButton implements ActionListener {
// Handle button click
public void actionPerformed(ActionEvent e) {
Object source = e.getSource(); // Get source object reference
if(source == pickButton) { // Is it the pick button?
int[] numbers = getNumbers(); // Get maxCount random numbers
for(int i = 0; i < numberCount; i++) {
luckyNumbers[i].setValue(numbers[i]); // Set the button values
}
} else if(source == colorButton) { // Is it the color button?

Color color = new Color(
flipColor.getRGB()^luckyNumbers[0].getBackground().getRGB());
893
Handling Events
21_568744 ch18.qxd 11/23/04 9:40 PM Page 893
for(int i = 0; i < numberCount; i++) {
luckyNumbers[i].setBackground(color); // Set the button colors
}
}
}
}
You no longer need to define a constructor, as the default will do. The actionPerformed() method
now decides what to do by comparing the reference returned by the
getSource() method for the event
object with the two button references in the
JButton fields of the Lottery class. With the previous ver-
sion of the listener class, you stored the ID as a data member, so a separate listener object was needed for
each button. In this case there are no data members in the listener class, so you can use one listener
object for both buttons.
The code to add these buttons in the
createGUI() method would then be:
// Add the two control buttons
Dimension buttonSize = new Dimension(100,20);
pickButton.setPreferredSize(buttonSize);
pickButton.setBorder(BorderFactory.createRaisedBevelBorder());
colorButton.setPreferredSize(buttonSize);
colorButton.setBorder(BorderFactory.createRaisedBevelBorder());
HandleControlButton controlHandler = new HandleControlButton();
pickButton.addActionListener(controlHandler);
colorButton.addActionListener(controlHandler);

controlPane.add(pickButton);
controlPane.add(colorButton);
content.add(controlPane);
The only fundamental difference here is that you use one listener object for both buttons.
There is another possible way to implement listeners for these buttons. You could define a separate class
for each listener — this would not be unreasonable, as the actions to be performed in response to the
semantic events for each button are quite different. You could use anonymous classes in this case — as I
discussed back in Chapter 6. You could do this by adding the listeners for the button objects in the
createGUI() method like this:
// Add the two control buttons
Dimension buttonSize = new Dimension(100,20);
pickButton.setPreferredSize(buttonSize);
pickButton.setBorder(BorderFactory.createRaisedBevelBorder());
colorButton.setPreferredSize(buttonSize);
colorButton.setBorder(BorderFactory.createRaisedBevelBorder());
pickButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {
int[] numbers = getNumbers();
894
Chapter 18
21_568744 ch18.qxd 11/23/04 9:40 PM Page 894
for(int i = 0; i < numberCount; i++) {
luckyNumbers[i].setValue(numbers[i]);
}
}
});
colorButton.addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent e) {

Color color = new Color(flipColor.getRGB()^luckyNumbers[0]
.getBackground().getRGB());
for(int i = 0; i < numberCount; i++) {
luckyNumbers[i].setBackground(color);
}
}
});
controlPane.add(pickButton);
controlPane.add(colorButton);
content.add(controlPane);
Now the two listeners are defined by anonymous classes, and the implementation of the
actionPerformed() method in each just takes care of the particular button for which it is listening.
This is a very common technique when the action to be performed in response to an event is simple.
Handling Low-Level and Semantic Events
I said earlier in this chapter that a component generates both low-level and semantic events, and you
could handle both if you want. I can demonstrate this quite easily with a small extension to the
Lottery
applet. Suppose you want to change the cursor to a hand cursor when it is over one of the selection but-
tons. This would be a good cue that you can select these buttons individually. You can do this by adding
a mouse listener for each button.
Try It Out A Mouse Listener for the Selection Buttons
There are many ways in which you could define the listener class. Here you’ll define it as a separate
class called
MouseHandler:
// Mouse event handler for a selection button
import java.awt.Cursor;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;
class MouseHandler extends MouseAdapter {
Cursor handCursor = new Cursor(Cursor.HAND_CURSOR);

Cursor defaultCursor = new Cursor(Cursor.DEFAULT_CURSOR);
// Handle mouse entering the selection button
public void mouseEntered(MouseEvent e) {
e.getComponent().setCursor(handCursor); // Switch to hand cursor
}
895
Handling Events
21_568744 ch18.qxd 11/23/04 9:40 PM Page 895

×