Part II
Copyright © 2003 by Charlie Calvert
This is the second part of a
three part article
.
Go to Part I
Go to Part III
Copyright © 2003 by Charlie Calvert
This is the second part of a
three part article
.
The ActionEvent Class
As you know, ActionPerformed methods
get passed a parameter of type ActionEvent:
void JButton1_actionPerformed(ActionEvent e)
The ActionEvent class contains a good
bit of information about the state of the mouse and keyboard when
this event was triggered. You will get a chance to explore many of
the more important features of this class in the next chapter, called
Firing Events. In that chapter, you will learn how to create
instances of this class, and how to use many of its methods. For now,
however, there are still a few basic things you ought to know about
the class.
Consider the methods of the ActionEvent
class shown in Table1.
|
e.getActionCommand();
|
Retrieve a string associated with
the command.
|
|
e.getModifiers();
|
Retrieve the state of the Alt and
Ctrl keys
|
|
e.getID();
|
Retreive an integer associated with
the command
|
|
e.getWhen();
|
Find out when the command was
issued.
|
|
e.getSource();
|
Which control caused the event? In
our case, JButton1.
|
NOTE: The e.getWhen() method is a new feature in JDK
1.4. That means that unless you have JBuilder 8, or unless you switch the
JDK to 1.4.0, you will not be able to use this portion of the code. You
need to use the IDE tools to switch your JDK. If you are using JBuilder
Personal, JDK switching cannot be accomplished on a per project basis,
instead you must change the JDK for the entire IDE. For more
information, see the source and notes for this book.
A program called ActionEventMadness, found with the source code that
accompanies this book, demonstrates many of the important methods
supported by the ActionEvent class. The key parts of that program are
examined over the course of the next few paragraphs.
Consider the following button response
method:
void JButton1_actionPerformed(ActionEvent e)
{
System.out.println("ParamString: " + e.paramString());
System.out.println("Action Command:" + e.getActionCommand());
int modifiers = e.getModifiers();
System.out.println("Modifiers: " + KeyEvent.getKeyModifiersText(modifiers));
System.out.println("ID: " + e.getID());
long a = e.getWhen();
Date d = new Date(a);
System.out.println("When: " + d);
System.out.println("Source: " + e.getSource().getClass().getName());
}
This method outputs the following information:
ParamString: ACTION_PERFORMED,cmd=JButton1,when=1019943559903,modifiers=Alt+Shift+Button1
Action Command:JButton1
Modifiers: Alt+Shift+Button1
ID: 1001
When: Sat Apr 27 14:39:19 PDT 2002
Souce: javax.swing.JButton
The paramString() method of the ActionEvent class gives
you some general information about the state of the button used to trigger
the event. The Action Command is a string associated with the button.
You can change this string in the inspector at design time.
The modifiers define which special keys
were selected at the time the button was pressed. Here I used the
KeyEvent.getKeyModifiersText method to find out the state of the
buttons. Here is another method that performs a similar task:
void JButtonBitMasks_actionPerformed(ActionEvent e)
{
String stateStr = "";
int keyState = e.getModifiers();
if ((keyState & InputEvent.ALT_MASK) != 0)
{
stateStr += "Alt is downn";
}
if ((keyState & InputEvent.SHIFT_MASK) != 0)
{
stateStr += "Shift is downn";
}
if ((keyState & InputEvent.CTRL_MASK) != 0)
{
stateStr += "Ctrl is downn";
}
if (stateStr.length() == 0)
{
stateStr = "You did not hold down Alt, Shift, or Ctrl";
}
jTextArea1.setText(stateStr);
}
This code uses the logical &
operator to perform bitwise operations revealing the state of the
pressed keys. For instance, this code will reveal whether or not the
Alt key was selected at the time the button that triggered the event
was pressed:
int keyState = e.getModifiers();
if ((keyState & InputEvent.ALT_MASK) != 0)
The id returned by a call to
ActionEvent.getID() represents a unique number associated with a
particular event.
A call to ActionEvent.getWhen() reveals the time when the
event occurred. Here is code that relies on the default invocation of the
Date.toString() method for translating the returned long integer into a
string:
long a = e.getWhen();
Date d = new Date(a);
System.out.println("When: " + d);
If you new to Java, you might think the code shown here is
a typo. That is not the case. By default, d.toString() is called. The
result of that call might look something like this:
When: Sat Apr 27 14:39:19 PDT 2002
Finally, the getSource method returns
an instance of the control that triggered the call to this event
handler. Call getSource.getClass.getName to get the type of class
that caused the event. Here is code to retrieve the caption of the
button that was pressed:
jTextArea1.append("nButton caption: " + ((JButton)(e.getSource())).getText());
Using other Standard Events
It should be obvious that JBuilder
supports more than the actionPerformed event. When you select a
particular component, and turn to the Events page in the inspector,
you are likely to see a range of event handlers that JBuilder will
create for you. For instance, JButton supports the
ancestorAdded, ancestorRemoved, itemStateChanged, stateChanged, etc,
events. Exploring all of these event handlers is a major undertaking,
and is really the task of a book on the JDK, not a book on JBuilder.
So I will leave it up to you to explore these various event handlers,
and to see what you can do with them.
Customizing the JBuilder Event Handling Mechanism
You have seen the technique that
JBuilder uses to handle events. In most cases, I suggest just going
with the flow, and allowing JBuilder to create your event handlers
for you, using the techniques just described. However, I want to take
a moment to point out two things:
There are other ways to handle
events that those shown so far in this chapter.
JBuilder may not help you create
these alternative kinds of event handlers, but it will not prevent
you from using them if you want.
The StringTest01 program, found in the ComplexProject
subdirectory found in this source for
this book, shows an alternative way to handle events. The event code
in that program was partially created by JBuilder, and partially created
by hand.
At the very bottom of Frame1.java,
you will find a class that looks like this:
/** I am using the same ActionListener for all of my menuitems in this
* application. Furthermore, I am sending most of them to the same
* event handler back in the main program.
*/
class MainListener implements ActionListener
{
Frame1 adaptee;
MainListener(Frame1 adaptee)
{
this.adaptee = adaptee;
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == adaptee.jMenuFileExit)
{
adaptee.jMenuFileExit_actionPerformed(e);
}
else if (e.getSource() == adaptee.jMenuHelpAbout)
{
adaptee.jMenuHelpAbout_actionPerformed(e);
}
else if (e.getSource() == adaptee.jMenuItemReverse)
{
adaptee.jMenuItemReverse_actionPerformed(e);
}
else if (e.getSource() == adaptee.jMenuItemGetFirstWord)
{
adaptee.jMenuItemReverse_actionPerformed(e);
}
else if (e.getSource() == adaptee.jMenuItemStripFirstWord)
{
adaptee.jMenuItemReverse_actionPerformed(e);
}
}
}
This class is very like the
Frame2_JButton1_actionAdapter class you saw earlier in this
chapter:
class Frame2_JButton1_actionAdapter implements java.awt.event.ActionListener
It is of the same type, and it is
declared in the same location. The only difference is that it has a
shorter name and calls not one event handler back in class Frame1,
but a whole series of them.
If you have been using Java for a
while, I'm sure you have seen code similar to this. It uses a method
of the ActionEvent object passed to the handler to sort out which
control in Frame1 actually called the class. The method it
uses is called getSource, and it returns the instance of the
class that called MainListener.
After the class decides which object
called it, then it uses that information to call the appropriate
method back in Frame1:
else if (e.getSource() == adaptee.jMenuItemStripFirstWord)
{
adaptee.jMenuItemReverse_actionPerformed(e);
}
There were several different ways to
associate the MainListener class with the various event-generating
components in Frame1. The technique this program uses looks
like this:
MainListener listen = new MainListener(this);
jMenuFileExit.addActionListener(listen);
jMenuItemReverse.addActionListener(listen);
jMenuItemGetFirstWord.addActionListener(listen);
etc
There is no way to make JBuilder
generate code of this type automatically. You have to write it
yourself. I simply created one event-handler using the JBuilder
tools, and then wrote the rest of the code by hand.
The technique shown here clearly uses
fewer resources than the technique that JBuilder uses. In particular,
it uses only one adapter class, while JBuilder code for handling this
kind of situation would generate several adaptor classes. On the
other hand, a pure OOP based language like Java is optimized to have
very little overhead when creating classes. The overhead created by
the adaptor classes is small enough to have no significant impact on
the vast majority of programs. So wasting a lot of time doing things
as I do here is usually not worth the candle. However, the option is
available.
In the long run, perhaps the most
important point is to see that JBuilder will allow you to customize
your code to whatever extent you want. Do things the JBuilder way if
you want, or go off in another direction if you prefer. JBuilder
gives you the freedom to do what you want.
Listener Interfaces and Adapter Classes
Very few classes get raw mouse events
automatically. In those few classes, if you turn to the Events page
of the JBuilder inspector, you will be able to create event handlers
for mouse events just as you create event handlers for the
actionEvent that occurs when the user clicks on a button.
NOTE: A JButton ActionPerformed event is
clearly caused by a button click, but it is not a button event in the
strict sense of the word. It tells you that the button was selected, and
if you explore the ActionEvent parameter passed to this method you can
find out about the state of the mouse and keyboard when the button was
selected. But still, the purpose of the event is not to tell you about
the mouse itself, but only that the user wanted to access the
functionality encapsulated in your response handler. In other words, its
primary purpose is to tell you that the button was selected.
But sometimes you will see that a class does not receive
events that you might want it to receive. For instance, you might want to
allow a JFrame descendant to receive mouse events. This section of
the chapter shows how to add mouse events to a class that does not already
provide support for mouse events in the events page of the inspector.
So far in this chapter you have had a chance to look at
both firing and receiving events. The events we have been looking at are
fairly straightforward. Some events, like those associated with the
MouseListener interface, are a bit more complex:
public interface MouseListener extends EventListener
{
public void mouseClicked(MouseEvent e);
public void mousePressed(MouseEvent e);
public void mouseReleased(MouseEvent e);
public void mouseEntered(MouseEvent e);
public void mouseExited(MouseEvent e);
}
By this point in the chapter, it should
be relatively clear what is going on in this interface. We have a
single event called a MouseEvent that gets sent to you on five
different occasions: when the mouse is clicked, pressed or released,
and also when the mouse enters or leaves the current window. You will
get an even deeper understanding of how all this occurs when you read
the next chapter, and start creating your own events and your own
listener interfaces.
NOTE: A clicked event occurs when the mouse has been
pressed and released in one motion, while the pressed event occurs when
the mouse has been pressed but not necessarily released.
KeyListener, MouseListener, MouseWheelListener
and MouseMotionListener
I'm going to show you a program found
on the CD called MouseKey that demonstrates how to set up a JFrame
descendant that supports the KeyListener, MouseListener,
MouseWheelListener, and MouseMotionListener interfaces. The program
is shown in Figure 3.
Figure 3: The MouseKey program supports the MDI
interface. It demonstrates techniques for accepting mouse and keyboard
input.
NOTE: The MouseWheelListener interface
described in this chapter is part of JDK 1.4. If you need to switch your
JDK, you will find instructions for that process in Chapter 1 and in the
index.html file that comes with the source. If
you are using JBuilder personal, then you may JDK switching for the entire
IDE if you want to use the MouseWheelListener interface inside of
JBuilder. Other versions of the product will allow you to switch JDKs on
a per project basis.
The MouseKey program uses an MDI
interface. Near the end of the chapter, I will explain exactly how to
create a program that uses this kind of interface. For now, you need
only know that there is a main window in the program, as shown in Figure 3. Residing on top
of this main window are three JPanel descendants. The lower of the
three panels is the InputPanel. You can type on this panel. The top
two panels will be used to display the input that occurs in the
InputPanel. In particular, when the user clicks in the InputPanel,
then a report of that mouse event will be shown in the MousePanel. If
the user types on the InputPanel, then a report of the user's
keypresses will be shown in the KeyPanel.
Understanding the MouseKey Program
You may want to follow along here and try to create your
own copy of the MouseKey program. I will not give you detailed
instructions on how to proceed, but I will try to give you enough
information so you should not find it hard to fill in the blanks by
yourself. The glue that holds the program together is MDI, or a virtual
desktop. I will explain how that glue works at the very end of this
chapter.
If building the program feels a bit too daunting, then
simply open the copy found with the source that accompanies this book. Run the
program a few times, and examine the code included in it so that you can
see how the text in the book relates to a real world program.
To create the program yourself, first
create a new project and new application, as described in Chapter 3,
"Default Applications and Applets. Choose File | New Class from
the JBuilder menu and create a JPanel descendant called
InputPanel.java. Figure 4 shows how to pick the base class and Figure
5 shows the correctly filled out class wizard. Click Ok to close the
wizard and then focus this class in the editor.
Figure 4: Browsing to select the class from which the InputPanel will descend.
Figure 5:
Filling out the class wizard to put a new JPanel descendant called
InputPanel in the untitled34 package.
If you are using the SE or enterprise
versions of JBuilder, choose Wizards | Implement Interface. Browse to
java.awt.event, and select the MouseMotionListener, MouseListener,
KeyListener, and (if you have JDK 1.4) MouseWheelListener interfaces,
as shown in Figure 6.
Figure 6: Selecting interfaces in the interface wizard.
The following code will be added near the top of your InputPanel.java file:
public class InputPanel extends JFrame
implements MouseListener, MouseMotionListener, MouseWheelListener, KeyListener
If you don't have the Interface Wizard
in your version of the product, then you will have to type this code
in manually. (Recall that MouseWheelListener is a JDK 1.4 only class.)
In the implementation of your class,
code similar to that shown in Listing 1 will be added by the
interface wizard. If you are using the Personal edition of JBuilder,
you will have to type in this code manually. The code is a little
lengthier than this, in that it includes more methods and provides
the name of the method that failed if an exception is raised. I've
pared it down just a bit here so that it will be easier to read.
Listing 1: The default implementation of the
MouseListener, MouseMotionListener and MouseWheelListener that is provided
by the Interface Wizard.
public void mouseClicked(MouseEvent e)
{
/**@todo Implement this java.awt.event.MouseListener method*/
throw new java.lang.UnsupportedOperationException("not yet implemented.");
}
public void mousePressed(MouseEvent e)
{
/**@todo Implement this java.awt.event.MouseListener method*/
throw new java.lang.UnsupportedOperationException("not yet implemented.");
}
public void mouseReleased(MouseEvent e)
{
/**@todo Implement this java.awt.event.MouseListener method*/
throw new java.lang.UnsupportedOperationException("not yet implemented.");
}
public void mouseEntered(MouseEvent e)
{
/**@todo Implement this java.awt.event.MouseListener method*/
throw new java.lang.UnsupportedOperationException("not yet implemented.");
}
public void mouseExited(MouseEvent e)
{
/**@todo Implement this java.awt.event.MouseListener method*/
throw new java.lang.UnsupportedOperationException("not yet implemented.");
}
public void mouseDragged(MouseEvent e)
{
/**@todo Implement this java.awt.event.MouseMotionListener method*/
throw new java.lang.UnsupportedOperationException("not yet implemented.");
}
public void mouseMoved(MouseEvent e)
{
/**@todo Implement this java.awt.event.MouseMotionListener method*/
throw new java.lang.UnsupportedOperationException("not yet implemented.");
}
public void mouseWheelMoved(MouseWheelEvent e)
{
/**@todo Implement this java.awt.event.MouseWheelListener method*/
throw new java.lang.UnsupportedOperationException("not yet implemented.");
}
All of the @todo items will show up at
the top of the structure pane under the node called todo, as shown in
Figure 7.
Figure 7: The todo items and import statements generated
by the Interface wizard all show up in the structure pane.
Connect with Us