Bruce Eckel’s Thinking in Java | Contents | Prev | Next |
noted previously,
action( )
isn’t the only method that’s automatically called by handleEvent( )
once it sorts everything out for you. There are three other sets of methods
that are called, and if you want to capture certain types of events (keyboard,
mouse, and focus events) all you have to do is override the provided method.
These methods are defined in the base class Component,
so they’re available in virtually all the controls that you might place
on a form. However, you should be aware that this approach is deprecated in
Java 1.1,
so although you might see legacy code using this technique you should use the
Java 1.1 approaches (described later in this chapter) instead.
action
(Event evt, Object what) |
When
the “typical” event occurs for this component (for example, when a button is pushed or a drop-down list item is selected) |
keyDown
(Event evt, int key) |
A
key is pressed when this component has the focus. The second argument is the key that was pressed and is redundantly copied from evt.key. |
keyUp(Event
evt, int key) |
A
key is released when this component has the focus. |
lostFocus(Event
evt, Object what) |
The
focus has moved away from the target. Normally, what is redundantly copied from evt.arg. |
gotFocus(Event
evt, Object what) |
The
focus has moved into the target. |
mouseDown(Event
evt,
|
A
mouse down has occurred over the component, at the coordinates x, y. |
mouseUp(Event
evt, int x, int y) |
A
mouse up has occurred over the component. |
mouseMove(Event
evt, int x, int y) |
The
mouse has moved while it’s over the component. |
mouseDrag(Event
evt, int x, int y) |
The
mouse is being dragged after a mouseDown occurred over the component. All drag events are reported to the component in which the mouseDown occurred until there is a mouseUp. |
mouseEnter(Event
evt, int x, int y) |
The
mouse wasn’t over the component before, but now it is. |
mouseExit(Event
evt, int x, int y) |
The
mouse used to be over the component, but now it isn’t. |
can see that each method receives an
Event
object along with some information that you’ll typically need when
you’re handling that particular situation – with a mouse event, for
example, it’s likely that you’ll want to know the coordinates where
the mouse event occurred. It’s interesting to note that when
Component’s
handleEvent( )
calls any of these methods (the typical case), the extra arguments are always
redundant as they are contained within the
Event
object. In fact, if you look at the source code for
Component.handleEvent( )
you can see that it explicitly plucks the additional arguments out of the
Event
object. (This might be considered inefficient coding in some languages, but
remember that Java’s focus is on safety, not necessarily speed.)
prove to yourself that these events are in fact being called and as an
interesting experiment, it’s worth creating an applet that overrides each
of the methods above (except for
action( ),
which
is overridden in many other places in this chapter) and displays data about
each of the events as they happen.
example also shows you how to make your own button object because that’s
what is used as the target of all the events of interest. You might first
(naturally) assume that to make a new button, you’d inherit from Button.
But this doesn’t work. Instead, you inherit from Canvas
(a much more generic component) and paint your button on that canvas by
overriding the paint( )
method. As you’ll see, it’s really too bad that overriding
Button
doesn’t work, since there’s a bit of code involved to paint the
button. (If you don’t believe me, try exchanging
Button
for
Canvas
in this example, and remember to call the base-class constructor
super(label).
You’ll see that the button doesn’t get painted and the events
don’t get handled.)
myButton
class is specific: it works only with an
AutoEvent
“parent window” (not a base class, but the window in which this
button is created and lives). With this knowledge,
myButton
can reach into the parent window and manipulate its text fields, which is
what’s necessary to be able to write the status information into the
fields of the parent. Of course this is a much more limited solution, since
myButton
can be used only in conjunction with
AutoEvent.
This kind of code is sometimes called “highly coupled.” However, to
make
myButton
more generic requires a lot more effort that isn’t warranted for this
example (and possibly for many of the applets that you will write). Again, keep
in mind that the following code uses APIs that are deprecated in Java 1.1.
//: AutoEvent.java // Alternatives to action() import java.awt.*; import java.applet.*; import java.util.*; class MyButton extends Canvas { AutoEvent parent; Color color; String label; MyButton(AutoEvent parent, Color color, String label) { this.label = label; this.parent = parent; this.color = color; } public void paint(Graphics g) { g.setColor(color); int rnd = 30; g.fillRoundRect(0, 0, size().width, size().height, rnd, rnd); g.setColor(Color.black); g.drawRoundRect(0, 0, size().width, size().height, rnd, rnd); FontMetrics fm = g.getFontMetrics(); int width = fm.stringWidth(label); int height = fm.getHeight(); int ascent = fm.getAscent(); int leading = fm.getLeading(); int horizMargin = (size().width - width)/2; int verMargin = (size().height - height)/2; g.setColor(Color.white); g.drawString(label, horizMargin, verMargin + ascent + leading); } public boolean keyDown(Event evt, int key) { TextField t = (TextField)parent.h.get("keyDown"); t.setText(evt.toString()); return true; } public boolean keyUp(Event evt, int key) { TextField t = (TextField)parent.h.get("keyUp"); t.setText(evt.toString()); return true; } public boolean lostFocus(Event evt, Object w) { TextField t = (TextField)parent.h.get("lostFocus"); t.setText(evt.toString()); return true; } public boolean gotFocus(Event evt, Object w) { TextField t = (TextField)parent.h.get("gotFocus"); t.setText(evt.toString()); return true; } public boolean mouseDown(Event evt,int x,int y) { TextField t = (TextField)parent.h.get("mouseDown"); t.setText(evt.toString()); return true; } public boolean mouseDrag(Event evt,int x,int y) { TextField t = (TextField)parent.h.get("mouseDrag"); t.setText(evt.toString()); return true; } public boolean mouseEnter(Event evt,int x,int y) { TextField t = (TextField)parent.h.get("mouseEnter"); t.setText(evt.toString()); return true; } public boolean mouseExit(Event evt,int x,int y) { TextField t = (TextField)parent.h.get("mouseExit"); t.setText(evt.toString()); return true; } public boolean mouseMove(Event evt,int x,int y) { TextField t = (TextField)parent.h.get("mouseMove"); t.setText(evt.toString()); return true; } public boolean mouseUp(Event evt,int x,int y) { TextField t = (TextField)parent.h.get("mouseUp"); t.setText(evt.toString()); return true; } } public class AutoEvent extends Applet { Hashtable h = new Hashtable(); String[] event = { "keyDown", "keyUp", "lostFocus", "gotFocus", "mouseDown", "mouseUp", "mouseMove", "mouseDrag", "mouseEnter", "mouseExit" }; MyButton b1 = new MyButton(this, Color.blue, "test1"), b2 = new MyButton(this, Color.red, "test2"); public void init() { setLayout(new GridLayout(event.length+1,2)); for(int i = 0; i < event.length; i++) { TextField t = new TextField(); t.setEditable(false); add(new Label(event[i], Label.CENTER)); add(t); h.put(event[i], t); } add(b1); add(b2); } } ///:~
can see the constructor uses the technique of using the same name for the
argument as what it’s assigned to, and differentiating between the two
using
this:
= label;
paint( )
method starts out simple: it fills a “round rectangle” with the
button’s color, and then draws a black line around it. Notice the use of
size( )
to determine the width and height of the component (in pixels, of course).
After this,
paint( )
seems
quite complicated because there’s a lot of calculation going on to figure
out how to center the button’s label inside the button using the
“font metrics.” You can get a pretty good idea of what’s
going on by looking at the method call, and it turns out that this is pretty
stock code, so you can just cut and paste it when you want to center a label
inside any component.
can’t understand exactly how the
keyDown( ),
keyUp( ),
etc. methods work until you look down at the
AutoEvent
class. This contains a Hashtable
to hold the strings representing the type of event and the
TextField
where information about that event is held. Of course, these could have been
created statically rather than putting them in a
Hashtable,
but I think you’ll agree that it’s a lot easier to use and change.
In particular, if you need to add or remove a new type of event in
AutoEvent,
you simply add or remove a string in the
event
array – everything else happens automatically.
place where you look up the strings is in the
keyDown( ),
keyUp( ),
etc. methods back in
MyButton.
Each of these methods uses the
parent
handle to reach back to the parent window. Since that parent is an
AutoEvent
it contains the
Hashtable
h
,
and the
get( )
method, when provided with the appropriate
String,
will produce a handle to an
Object
that we happen to know is a
TextField
– so it is cast to that. Then the
Event
object is converted to its
String
representation, which is displayed in the
TextField.