Event Handling
Copyright © 2002 by Charlie Calvert
Go to Part I
Go to Part II
Go to Part III
Get the source for the whole book.
In this chapter and the next you are
going to learn the basic facts about handling events. The text will
cover four major topics, the first two in this chapter, the last two
in the next:
Basic Event Handling: Learn
the mechanism that JBuilder employs to allow you to handle the most
basic events generated by a particular class. For instance, you will
learn how to respond to a click on a JButton. More
importantly, you learn how the code that supports this kind of
simple event handler is constructed, and the various options you
will have when implementing it. In particular, you will have a
chance to examine the syntax for both anonymous and standard adapter
classes.
Supporting Listener Interfaces:
Learn to create a class that supports an interface such as
MouseListener, KeyListener, or the JDK 1.4 class called
MouseWheelListener. These interfaces can enable a class to respond
to a wide range of events.
Generating Events: Create a
class that can not only receive events, but also fire events. This
technology can be used as a form of loosely coupled communication
between the classes in your program. Loose coupling of this kind is
highly respected in the OOP programming world because it promotes
reuse and helps develop robust architectures.
Custom Events: Look at how to create custom
events that are tailor-made to convey a particular type of
information.
JBuilder provides easy to use mechanisms for handling most
of these technologies. This chapter will explore these techniques, and
give you some tips on how to expedite event-based development.
Java events are not always an easy subject, especially for
people new to the Java language. The level of complexity involved is
significant, but not mind-boggling. A modest level of effort in this area
will yield big results in the long run. Furthermore, JBuilder makes this
topic much simpler than it would be were you trying to write
event-handling code by hand.
NOTE: Not all of the techniques shown in this chapter
are supported in the Personal version of JBuilder. However, I will show
all the code that the JBuilder wizards generate. If necessary, you can
simply type the code in by hand. The MouseWheelListener class is found
only in JDK 1.4.
Basic Event Handling
You already know the simplest way to
handle events in JBuilder. However, we should go over it one more
time just to be sure that we understand exactly how this code is
structured.
NOTE: In this chapter you are going to learn how
to make a JButton object respond to events. You will also learn
about the ActionEvent object. I will not, however, include descriptions
of how to make a JButton respond to key presses, or how to make it
respond to a key combination, such as Alt-X. I will also not talk about
changing the text of the button, using HTML with the button, or adding
icons to the button. All of these latter subjects are discussed later in
this section of the book. In particular, see Chapter 18, called "The
Art of the Button."
Create a new project and place inside it a new application
that includes a JMenuBar. The ability to create a menu bar as part of the
application is built into the JBuilder Application wizard, which was
described in the "Default Applications and Applets" chapter back
in the Getting Started section of this book.
Make sure Frame1 has its layout set to the default
BorderLayout. Drop a JButton down on Frame1. Make sure the
constraints for the button are set to Center. At this stage, your
application is all button. When you run it, it should look like the
application shown in Figure 1.
Figure 1: A default JBuilder project with a button dropped down onto Frame1.
We now want to create a simple event that can be
associated with this button. More specifically, when the user presses
this button, we want our event to be called and some associated action to
take place. For instance, we might pop up a dialog that will allow the
user to choose a new application.
In JBuilder, there are two ways to associate an event with the button:
Double click on the button. This
will automatically take you to the source for your program and
create a method handler stub for you.
Turn to the Events page of the
inspector and click on the actionPerformed property. A single click
should give you an editable suggested name for your property. Use
this single click technology to specify the name for the event
handler you are about to create. You can go with the default if you
want, but you can use this opportunity to give your event handler a
more meaningful name. A double click or a press of the enter key
will take you to source view and create the method stub for you.
Regardless of which course you take,
the method stub that was created should look something like this,
though the actual name of the method will vary:
void JButton1_actionPerformed(ActionEvent e)
{
}
Fill in the method with some simple
code so that you can see it in action:
void JButton1_actionPerformed(ActionEvent e)
{
JOptionPane.showMessageDialog(this, "Hate is not conquered by hate, hate is conquered by love!");
}
The JButton1_actionPerformed
method will be called automatically when JButton1 is selected
by the user.
NOTE: The JOptionPane class is designed to help
you build pop up dialogs of various types. It has methods such as
showMessageDialog, showInputDialog, showConfirmDialog
and so on. This book is about JBuilder, and not about the JDK, so I will
ask that you read the help or visit Sun's web site if you want to learn
more about this class. There are many examples of using the class in the
source code
for this book, so you can grep through that code if you want to see the
class in action.
NOTE: Before
moving on to the next subject, I should perhaps mention that there
are occasions when you will get error messages when trying to create
an event handler. For instance, you might double click on JButton1
and receive a seemingly nonsensical error saying that a parenthesis
was not found. If something like this happens to you, try closing the
file you are working on, and then opening it again from the recently
used files list. This usually fixes the problem for me.
How Events Get Called
The JButton1_actionPerformed
method in the previous section is not called directly by the JDK.
Instead, it is called by a class embedded inside of Frame1. It
is now time to spend a moment thinking about that class.
The class that calls
JButton1_actionPerformed can appear in one of two places in
your source code. Which of the places it resides depends on the
settings you selected in the Project Properties dialog reached by
selecting Project | Project Properties | Code Style from the
JBuilder menu. The dialog, which was mentioned in the "Product
Overview" chapter, is shown in Figure 2.
Figure 2: Selecting the style for event-handling adapters in the Project Properties dialog.
To understand exactly what is happening
now, you need to take a look at two copies of the complete source
code for a JFrame with a button on it and one response method.
The first version is found in Listing 1, the second in Listing 2.
Listing 1: The source for Frame1 as it appears
when you use standard adapters.
package untitled32;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Frame2 extends JFrame
{
JButton JButton1 = new JButton();
public Frame2()
{
try
{
jbInit();
} catch(Exception e)
{
e.printStackTrace();
}
}
private void jbInit() throws Exception
{
JButton1.setText("JButton1");
JButton1.addActionListener(new Frame2_JButton1_actionAdapter(this));
this.getContentPane().add(JButton1, BorderLayout.CENTER);
}
void JButton1_actionPerformed(ActionEvent e)
{
}
}
class Frame2_JButton1_actionAdapter implements java.awt.event.ActionListener
{
Frame2 adaptee;
Frame2_JButton1_actionAdapter(Frame2 adaptee)
{
this.adaptee = adaptee;
}
public void actionPerformed(ActionEvent e)
{
adaptee.JButton1_actionPerformed(e);
}
}
Listing 2: The source for Frame1 as it appears
when you use anonymous adapters.
package untitled32;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
/**
* <p>Title: </p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2002</p>
* <p>Company: </p>
* @author unascribed
* @version 1.0
*/
public class Frame2 extends JFrame
{
JButton JButton1 = new JButton();
public Frame2()
{
try
{
jbInit();
} catch(Exception e)
{
e.printStackTrace();
}
}
private void jbInit() throws Exception
{
JButton1.setText("JButton1");
JButton1.addActionListener(new java.awt.event.ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JButton1_actionPerformed(e);
}
});
this.getContentPane().add(JButton1, BorderLayout.CENTER);
}
void JButton1_actionPerformed(ActionEvent e)
{
}
}
I created these two source files by
first choosing File | New from the JBuilder menu. In the
General page of the Object Browser, I then chose Frame, which created
a new instance of the JFrame class. In Listing 1, the Code
Style for Event Handling in the project was set to Standard Adapters,
and in Listing 2, it was set to Anonymous Adapters. I then dropped a
button on each frame and created a handler for it.
The reason I created new JFrame
descendants rather than working with a Frame1 instance was so
that these examples would not include irrelevant code such as the
processWindowEvent method. This code is stripped down so that it
shows little detail other than the code we want to focus on.
In Listing 1, notice that there is a
class called Frame2_JButton1_actionAdapter:
class Frame2_JButton1_actionAdapter implements java.awt.event.ActionListener
{
Frame2 adaptee;
Frame2_JButton1_actionAdapter(Frame2 adaptee)
{
this.adaptee = adaptee;
}
public void actionPerformed(ActionEvent e)
{
adaptee.JButton1_actionPerformed(e);
}
}
Look further and you will see that this
class calls the JButton1_actionPerformed method that we had
created earlier:
adaptee.JButton1_actionPerformed(e);
What we have established so far is that
there is a class called an adapter included in your source code that
calls the button handler. The button handler is where you place the
code you want to have called when the button is pressed.
So how does the JDK know to call your
adapter when the button is pressed? The following code in jbInit
supplies part of the answer to that question:
JButton1.addActionListener(new Frame2_JButton1_actionAdapter(this));
The addActionListener method
exists deep inside the JDK. This method calls it, and assigns the
Frame2_JButton1_actionAdapter class to this instance of the
JButton class. The JButton1 class maintains a list of
all the actionAdapters that have been registered with it. When the
button itself is pressed, it looks at that list and calls the
actionPerformed method of that class:
public void actionPerformed(ActionEvent e)
{
adaptee.JButton1_actionPerformed(e);
}
So now the mystery has been resolved. We have established
that the JDK knows to call the adapter class because you called
addActionListener, and we have seen that the adapter class calls
our JButton1_actionPerformed method. So far so good.
NOTE: You will, in fact, learn more about this process
in the next chapter. There you will -- with the help of JBuilder --
create your own custom instances of the addActionListener method.
But for now, you have enough information to see how the system works.
You might also be interested in viewing the first method of the adapter:
Frame2 adaptee;
Frame2_JButton1_actionAdapter(Frame2 adaptee)
{
this.adaptee = adaptee;
}
This method is the constructor. It is passed an instance
of the main frame for your application back in jbInit:
JButton1.addActionListener(new Frame2_JButton1_actionAdapter(this));
The word this is the reference
to Frame2 that gets passed to the adapter's constructor.
The frame is then assigned to the
variable adaptee. The adaptee variable is used in the actionPerformed
method to call JButton1_actionPerformed.
NOTE: In case you are curious, I believe that adaptee
is not an English word. It is, however, a proper French word. I'm sure
that the multicultural and very sophisticated JBuilder developers were in
fact thinking in French when they came up with this variable name!
At this stage, you now know the whole story of how events
work when you use Standard Adapters in a JBuilder application. I believe
that all of the code in this book, unless explicitly marked otherwise,
uses standard adapters.
Anonymous Adapters
Anonymous Adapters, shown in Listing two, use an anonymous
class instead of standard Java class. The anonymous class is created in
jbInit:
JButton1.addActionListener
(
new java.awt.event.ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JButton1_actionPerformed(e);
}
}
);
Anonymous classes are created, declared and implemented
inside the parameter of another method. The method in this case looks
like this:
JButton1.addActionListener(-- class inserted here --)
These are standard parenthesis, as if you were simply passing a parameter
to a method. The class that has been inserted into
the parameter for this method looks like this:
new java.awt.event.ActionListener()
{
public void actionPerformed(ActionEvent e)
{
JButton1_actionPerformed(e);
}
}
Note the existence of the word new
before the class declaration, and note that the code uses curly braces rather than
parens, and note further that the ancestor
class name is given, but the class itself has no name: it is
anonymous. The class is derived from ActionListener, but the child
class has no name.
As you can see, this class is very
similar to the adapter class from Listing 1. It has no constructor
and no Francophile adaptee variable. Nevertheless, its structure
should be very familiar. The adaptee variable is not needed, since
this class lives inside the scope of Frame2, and hence can call its
methods directly. In fact, this class has an actionPerformed method
that directly calls JButton1_actionPerformed:
public void actionPerformed(ActionEvent e)
{
JButton1_actionPerformed(e);
}
Once again, the
JButton1.addActionListener method inserts a handle to this
anonymous class into a list maintained by JButton1. When
JButton1 is pressed, all the classes in that list have their
actionPerformed method called. What happens inside that method
is up to us. In this case, we have that method call
JButton1_actionPerformed. Or rather, JBuilder
automatically generates code that will call JButton1_actionPerformed.
Anonymous Adapters vs Standard Adapters
When writing this book, and when
developing the example programs, I had to decide whether I wanted to
use anonymous adapters or standard adapters. As you already know, I
chose to use standard adapters.
Why did I make that decision? Simply because to me
Standard Adapters are easier to parse visually than anonymous adapters.
Furthermore, the use of Standard Adapters helps to keep my jbInit
methods free of clutter.
I make absolutely no claim that my choice here is better
than its opposite. To my mind, this is simply a matter of taste, and my
taste tends toward standard adapters. You should examine your own mind,
and use the technique that you prefer. In the long run, it is all pretty
much six of one, half dozen of the other. You should just pick your
favorite technology and go along with it as happily as possible.
Go to Part I
Go to Part II
Go to Part III
Get the source for the whole book.