SCOUG OS/2 For You - May 1999
Cup of Java
Event Handling
One of the most important aspects of most non-trivial applications (especially UI type apps) is the ability to respond to events that are generated by the various components of the application, both in response to user interactions and other system components such as client-server processing. In this article we will look at how Java supports event generation and handling and how to create (and process) custom events.
Event Basics
There are three parts to the event model in Java 1.1. They are:
- (1) Event object
- this is an instance of a Java class that contains the characteristics of the event. For example, if the event is generated from clicking a mouse button, the event object would contain information such as the coordinates of the mouse cursor, which button was clicked, how many times it was clicked, etc..
- (2) Dispatching class/method
- this is an object which detects that an event has occurred and is then responsible for notifying other objects of the event, passing the appropriate event object to those objects. These other objects are "listeners" for the event. Most AWT components, such as Button, List, Textfield, etc. are examples of dispatching classes.
A Button, for instance, is capable of notifying other components when it is "pushed." These classes will typically have a set of two methods that can be invoked by would-be "listeners": one to tell the class that the object wants to listen and another to tell the class that the object no longer wants to listen.
These methods are conventionally named:
add...Listener (to add an object as a listener)
remove...Listener (to remove the object as a listener)
where ... is the generic type of event to listen for. In the case of the Button, this would be "Action" to indicate the action of pushing the button. (So the methods would be addActionListener and removeActionListener).
- (3) Listener Interface
- for the dispatching to work properly, the dispatching class must be able to rely on each of its listeners to contain the method that it executes when the event occurs. This is easily accomplished in Java through the use of an Interface class. The details of the interface are determined by the type of events that it supports.
The important point is that a class which is going to be a listener must implement that interface (which in turn means that it must define all of the interface's methods).
A Simple Example
The AWT Button class can be used to demonstrate the use of events. As mentioned above, the Button class generates an ActionEvent when "pushed." Since the Button class itself generates and dispatches these events, you only need to tell a button that you want to be notified and then code the required interface method; in this case there is only one. The interface associated with ActionEvent is ActionListener and its one method is Action-Performed.
(NOTE: all of the AWT events are defined in the package named java.awt.event for JDK 1.1.)
So, the following code would be added to your class to allow it to perform processing when a button is pushed:
import java.awt.*;
import java.awt.event.*;
// define a Frame and allow it to handle Action events
public class MyClass
extends Frame implements ActionListener
// define a Button object
Button myButton = new Button(...)
// tell the Button object that the Frame component is to
// be notified when the button is pushed:
myButton.addActionListener(this);
// add the required method for the ActionListener
// interface
public void ActionPerformed(ActionEvent aev) {
// specific processing in the method for the myButton
// object
if (aev.getSource() == myButton) {
... processing
return;
}
Event Categories
For the AWT package, there are a number of events supported. These events are broken down into categories that loosely represent the types of actions that a GUI application needs to process. The events and their listener interfaces are actually defined in another package, awt.event.
It is not practical to list the details here, but the categories are:
- ActionEvent
- handles controls that can be "pushed" such as buttons and menu items
- AdjustmentEvent
- handles controls that are "adjustable" such as a Scrollbar
- ComponentEvent
- handles component level events such as moving, resizing, displaying, etc.
- ContainerEvent
- handles container level events such as adding or removing components
- FocusEvent
- handles focus gained or lost events
- ItemEvent
- handles selectable controls such as List, Choice
- KeyEvent
- handles keyboard events
- MouseEvent
- handles mouse button events and mouse movement into or out of a component
- MouseMotionEvent
- handles mouse drag and movement events
- TextEvent
- handles text component events such as text changed
- WindowEvent
- handles generic window events such as open, close, activate and deactivate
What if I'm lazy?
Sometimes coding all of the methods for an interface can be time consuming (or at least boring); especially when you are just interested in one of them. To simplify this effort, the JDK provides a pre-defined set of classes called Adapters which define all of the methods for the corresponding Listener interface, each of which does nothing. You can then simply create your own subclassed version of the Adapter and override the one (or two) methods that you are interested in.
For example, to implement only the MousePressed method you could define a class such as:
public class MyMouseAdapter extends MouseAdapter {
public void mousePressed(MouseEvent e) {
... custom processing ...
}
}
Custom Events
The above information paves the way to understanding how to create our own custom event and handlers. Suppose we were building an application which interacts with the SCOUG membership database. We would have a specific class, AccessMembers, that does the actual database interaction, and another class ScougMember which contains the details of a specific member. We now want to set up event handling that will allow other parts of this application to do processing when the database is accessed.
To accomplish this we would need to:
(1) define an event class that encapsulates the information for the database interaction events (eg MemberEvent). A sample, which provides the member class and status code (ie., whether the operation was successful or not) would be:
public class MemberEvent {
private ScougMember member;
private int statusCode;
MemberEvent(ScougMember m, status s) {
member = m;
statusCode = s;
}
public ScougMember getMember() {
return member;
}
public int getStatusCode() {
return statusCode();
}
}
(2) define an interface that supports the various forms of the event (eg MemberListener). For simplicity, we will just handle three forms:
add a new member (addMember method)
modify an existing member (modifyMember method)
delete a member (deleteMember method)
A sample definition:
public Interface MemberListener {
public void memberAdded(MemberEvent mv);
public void memberModified(MemberEvent mv);
public void memberDeleted(MemberEvent mv);
}
(3) add code to the AccessMembers class to support the creation of the MemberEvent and notification to all currently registered listeners. This can be somewhat complex, but the general requirements are:
//define a Vector to hold the currently active listeners:
private Vector listeners = new Vector();
//provide methods to add/remove listeners:
public synchronized void addMemberListener(MemberListener ml) {
listeners.addElement(ml);
}
public synchronized void removeMemberListener(MemberListener ml) {
listeners.removeElement(ml);
}
//add methods to notify listeners when the event occurs
//example here is when a member is modified:
private synchronized void notifyModified(ScougMember m, status s) {
MemberEvent mev = new MemberEvent(m, s);
Enumeration en = listeners.elements();
while (en.hasMoreElements()) {
((MemberListener) en.nextElement()).userModified(mev);
}
}
(4) add code to the other components of the applica-tion to receive and process the MemberEvents. This coding is similar to the simple Button example given above:
public class Whatever implements MemberListener { ...
//must have an access object (either set or passed)
private AccessMember access;
//something must set the access object:
public void setAccessMember(am) {
access = am;
}
...
//register this class to listen
access.addMemberListener(this);
//define all three methods of the interface
public void userAdded(MemberEvent m) { ... }
public void userModified(MemberEvent m) { ... }
public void userDeleted(MemberEvent m) { ... }
Then, the code in each of these three methods would automatically be invoked whenever the appropriate event occurred within the access object.
Summary
Event handling in JDK 1.1 is based on the concepts of event listeners and notifiers. For them to communicate properly, an Event object and Listener interface are used. This model provides a robust and extensible capability which can be applied to non-visual events as well as GUI ones.
Cup of Java by Terry Warren started last month with
Threads and Java.
Next month, the topic is Zipping Along.
The Southern California OS/2 User Group
P.O. Box 26904
Santa Ana, CA 92799-6904, USA
Copyright 1999 the Southern California OS/2 User Group. ALL RIGHTS
RESERVED.
SCOUG, Warp Expo West, and Warpfest are trademarks of the Southern California OS/2 User Group.
OS/2, Workplace Shell, and IBM are registered trademarks of International
Business Machines Corporation.
All other trademarks remain the property of their respective owners.
|