Bruce Eckel's Thinking in Java Contents | Prev | Next

Alternatives to action

As 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.

Component method

When it’s called

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,

int x, int y)

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.

You 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.)

To 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.

This 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.)

The 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);
  }
} ///:~ 

You 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:

this.label = label;

The 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.

You 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.

The 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.

It turns out this example is rather fun to play with since you can really see what’s going on with the events in your program.

Contents | Prev | Next