To manage the user's interaction with the components that make up the GUI for a program, we must understand how events are handled in Java. To get an idea of how this works, let's consider a specific example. Don't worry too much about the class names and other details here. Just try to get a feel for how things connect together.
Suppose the user clicks a button in the GUI for your program. The button is the source of this event. The event generated as a result of the mouse click is associated with the JButton object in your program that represents the button on the screen. An event always has a source object – in this case the JButton object. When the button is clicked, it will create a new object that represents and identifies this event – in this case an object of type ActionEvent. This object will contain information about the event and its source. Any event that is passed to a Java program will be represented by a particular event object – and this object will be passed as an argument to the method that is to handle the event.
The event object corresponding to the button click will be passed to any listener object that has previously registered an interest in this kind of event – a listener object being simply an object that listens for particular events. A listener is also called a target for an event. Here, 'passing the event to the listener' just means the event source calling a particular method in the listener object and passing the event object to it as an argument. A listener object can listen for events for a particular object – just a single button for instance, or it can listen for events for several different objects – a group of menu items for example. Which approach you take depends on the context, and which is most convenient from a programming point of view. Your programs will often involve both.
So how do you define a listener? You can make the objects of any class listener objects by making the class implement a listener interface. There are quite a variety of listener interfaces, to cater for different kinds of events. In the case of our button click, the ActionListener interface needs to be implemented to receive the event from the button. The code that is to receive this event object and respond to the event is implemented in a method declared in the listener interface. In our example, the actionPerformed() method in the ActionListener interface is called when the event occurs, and the event object is passed as an argument. Each kind of listener interface defines particular methods for receiving the events that that listener has been designed to deal with.
Simply implementing a listener interface isn't sufficient to link the listener object to an event source. You still have to connect the listener to the source, or sources, of the events that you want it to deal with. You register a listener object with a source by calling a particular method in the source object. In this case, we call the addActionListener() method for the JButton object, and pass the listener object as an argument to the method.
This mechanism for handling events using listeners is very flexible, and very efficient, particularly for GUI events. Any number of listeners can receive a particular event. However, a particular event is only passed to the listeners that have registered to receive it, so only interested parties are involved in responding to each event. Since being a listener just requires a suitable interface to be implemented, you can receive and handle events virtually anywhere you like. The way in which events are handled in Java, using listener objects, is referred to as the delegation event model. This is because the responsibility for responding to events that originate with a component, such as a button or a menu item, is not handled by the objects that originated the events themselves – but is delegated to separate listener objects.
Not all event handling necessarily requires a separate listener. A component can handle its own events, as we shall see a little later in this chapter.
A very important point to keep in mind when writing code to handle events is that all such code executes in the same thread, the event-dispatching thread. This implies that while your event-handling code is executing, no other events can be processed. The code to handle the next event will only start executing when the current event-handler finishes. Thus the responsiveness of your program to the user is dependent on how long your event-handling code takes to execute. For snappy performance, your event handlers must take as little time as possible to execute.
Let's now get down to looking at the specifics of what kinds of events we can expect, and the range of listener interfaces that process them.