Section 6.2
Mouse and Keyboard Events and Listeners


LIKE MOST EVERYTHING IN JAVA, EVENTS are represented by objects. There are several types of events, and each type is represented by a class. All the event classes used in the AWT are subclasses of an abstract class named java.awt.AWTEvent. Event classes that represent particular types of events, such as MouseEvent and ActionEvent, are found in the package java.awt.event.

For an event to have any effect, a program must detect the event and react to it. For each event class, there is an interface that defines one or more methods for reacting to events of that class. For example, associated with the class ActionEvent is an interface named ActionListener. This interface defines a method, "public void actionPerfromed(ActionEvent evt)". An object that wants to respond to action events must implement this interface. For example, if an applet named MyApplet is to respond to action events, then the definition of the applet class has the form:

       public class MyApplet extends Applet implements ActionListener {
       
          . . .   // other variables, methods, etc.
          
          public void actionPerformed(ActionEvent evt) {
          
             . . . // respond to the action event, evt
             
          }
          
          . . . // other variables, methods, etc.
          
       }

By implementing the ActionListener interface, an object becomes capable of "listening" for action events. But before it can actually "hear" any events, it must be registered as an action listener. Events are generated by components. When the user clicks on a button, for example, an action event is generated. Each component that can generate action events has a method called addActionListener. This method is used to register action listeners with the component. When the component generates an action event, it notifies any registered action listeners by calling their actionPerformed() methods. (There can be more that one listener, but it would be somewhat unusual.)

For example, if MyApplet creates a button, commandButton, and wants to be notified when the user clicks on that button, then the applet must call the method commandButton.addActionListener(this). The word "this" refers to the applet itself, which means that the notification will be sent to the applet, by calling the actionPerfomed() method of the applet. The obvious place for the applet to create the commandButton and set itself up as a listener would be in its init() method, which would look something like this:

       public void init() {   // initialization method for MyApplet
          . . . // other stuff
          commandButton = new Button("Do It!");
          commandButton.addActionListener(this);
          . . . // add commandButton to the applet or to some other container
          . . . // more stuff
       }

Of course, any object can play the roll of action listener for the button, as long as it implements the ActionListener interface. It doesn't have to be the applet that contains the button, and it doesn't even have to be a GUI component.

When the actionPerformed() method is called, a parameter of type ActionEvent is passed to it. This parameter carries information about the particular event that has occurred. Methods for accessing this information are defined in the ActionEvent class. Each type of event is represented by a different class because each type of event requires different information to be sent to listeners.

In this section and the next, I'll discuss the various event classes and their associated listeners. The next section covers specialized components like scroll bars and text-input boxes. The user interacts with one of these specialized components using the mouse or keyboard. The component detects that interaction and converts it into a specialized event appropriate to the component. For example, when the user clicks on a button, the button converts that action into an ActionEvent. However, there are less specialized, lower level events associated with the mouse and keyboard, and you can program responses to them. A button, of course, has already been programmed to respond to mouse events by sending an ActionEvent, and there is generally no need to change that programming. However, it is common to program a canvas or an applet to respond to mouse and keyboard events, and I'll explain how to do that in this section.


Mouse Events

There are actually two event classes associated with the mouse: MouseEvent and MouseMotionEvent. There are also two listener interfaces, MouseListener and MouseMotionListener. The MouseListener interface declares the methods

          public void mousePressed(MouseEvent evt);
          public void mouseReleased(MouseEvent evt);
          public void mouseClicked(MouseEvent evt);
          public void mouseEntered(MouseEvent evt);
          public void mouseExited(MouseEvent evt);

and the MouseMotionListener declares

          public void mouseMoved(MouseEvent evt);
          public void mouseDragged(MouseEvent evt);

Any component can generate mouse events. An object that wants to respond to these events must implement one or both of the listener interfaces. It must also register itself with the component by calling the component's addMouseListener() and/or addMouseMotionListener() methods. Note that an object that implements MouseListener must provide definitions for all five of the methods in that interface, even if a definition consists just of an empty set of braces. Similarly, an object that implements MouseMotionListener must define both the mouseMoved() and the mouseDragged() methods.

A component calls mousePressed() whenever one of the buttons on the mouse is pressed while the mouse cursor is over that component. It will then call the mouseReleased() method when the button is released -- even if the cursor has moved outside of the component by that time. The mouseClicked() method is called if the button is pressed and released at the same point; it is called in addition to mousePressed() and mouseReleased(). If you simply want to respond to mouse clicks, you should probably do so in the mousePressed() routine, and leave the definitions of mouseReleased() and mouseClicked() empty. (Use mouseClicked() instead if you want to give the user a chance to change his mind by moving the mouse cursor before releasing the button.)

The mouseDragged() method is called when the user moves the mouse while holding down one of the buttons on the mouse. Any number of calls to mouseDragged() can occur between a call to mousePressed() and a call to mouseReleased(). Note that mouse-dragged events come from the component where the mouse button was originally pressed, even if the mouse cursor has moved outside that component.

I have rarely found a practical use for mouseEntered(), mouseExited(), and mouseMoved(). These have to do with movement of the mouse while no mouse button is pressed. They have obvious meanings. You could, for example, use mouseEntered() and mouseExited() to make a component change its appearance when the mouse cursor is in that component. In an arcade-style game, you might use mouseMoved() to move a gun around and then use the MousePressed() method to fire the gun when the user clicks the mouse.

Looking at a simple example might help to make things clearer. Suppose you want a canvas that changes color when the user clicks on it. This could be done as follows:

       class ColorChangeCanvas extends Canvas implements MouseListener {
       
          int currentColor = 0;  // 0 for red, 1 for blue, 2 for green
          
          ColorChangeCanvas() { // constructor
             setBackground(Color.red);
             addMouseListener(this);  // Canvas will listen for
                                      //   its own mouse events.
          }
          
          public void mousePressed(MouseEvent evt) {
                // User has clicked on the canvas;
                // cycle its background color and repaint it.
             currentColor++;
             if (currentColor > 2)
                currentColor = 0;
             switch (currentColor) {
                case 0: setBackground(Color.red); break;
                case 1: setBackground(Color.blue); break;
                case 2: setBackground(Color.green); break;
             }
             repaint();
          }
          
          public void mouseReleased(MouseEvent evt) { }   // Definitions required
          public void mouseClicked(MouseEvent evt) { }    //   by the MouseListener
          public void mouseEntered(MouseEvent evt) { }    //   interface.
          public void mouseExited(MouseEvent evt) { }
        
       } // end class ColorChangeCanvas   

In this case, the same component that generates the mouse events is listening for them. This is not uncommon for mouse and keyboard events.

Often, when a mouse event occurs, you want to know the location of the mouse cursor. This information is available from the parameter, evt, of type MouseEvent. To find the coordinates of the mouse cursor, call evt.getX() and evt.getY(). These methods return integers which give the x and y coordinates in the component's coordinate system, in which the top left corner is (0,0).

In some cases, you'd also like to know which button on the mouse was pressed. The method evt.isMetaDown() returns a boolean value that is true if the right button on the mouse is pressed. Similarly, evt.isAltDown() returns true if the middle button is down. Now, not every mouse has a middle button and a right button. A user can simulate pressing the middle mouse button by holding down the ALT key (on a Macintosh, the Option key) while using the mouse. This will work even on a computer that does have a middle mouse button. Similarly, holding down the Meta key (the Command key on a Macintosh; no equivalent under Windows) simulates pressing the right mouse button. You can also tell whether the user is holding down the shift key or the control key by calling evt.isShiftDown() and evt.isControlDown().

Here is a small sample applet that displays information about mouse actions. It is programed to respond to any of the seven diffent kinds of mouse events by displaying the coordinates of the mouse, the type of event, and a list of the modifier keys that are down (Shift, Control, Meta, and Alt). Experiment to see what happens when you use the mouse on the applet. The source code for this applet can be found in SimpleTrackMouse.java.

One of the operations that can be done with a mouse is dragging. "Dragging" refers to moving the mouse while holding down a mouse button. If you want a component to respond to dragging, you generally need to write three methods: mousePressed(), mouseDragged(), and mouseReleased(). Your response to one dragging gesture is spread out over three methods. Furthermore, the mouseDragged() method can be called many times as the mouse moves. You need to set up some instance variables to keep track of what is going on between method calls. Suppose, for example, that you want to let the user sketch a curve by dragging the mouse. Each time the mouse moves as it is being dragged, you have to draw a line between the previous mouse location and the current mouse location. To do this, you need to keep track of the previous mouse location in a pair of instance variables. Here is an outline of how you could write the program:

        int prev_x;  // previous x coordinate of mouse
        int prev_y;  // previous y coordinate of mouse
        
        public void mousePressed(MouseEvent evt) {
           prev_x = evt.getX();  // record mouse location
           prev_y = evt.getY();
        }
        
        public void mouseDragged(MouseEvent evt) {
           int new_x = evt.getX();
           int new_y = evt.getY();
           ... // draw a line from (prev_x,prev_y) to (new_x,new_y)
           prev_x = new_x;
           prev_y = new_y;
        }
        
        public void mouseReleased(MouseEvent evt) {
           int new_x = evt.getX();
           int new_y = evt.getY();
           ... // draw a line from (prev_x,prev_y) to (new_x,new_y)
        }

Here's a trivial sketching program that uses this technique. The applet uses an off-screen image to store a copy of the sketch, so that it can be redrawn when necessary (for example, if you cover up the applet with another window and then uncover it). Whenever a line needs to be drawn, it is drawn both on the applet itself and on the backup copy in the off-screen image. As an added feature in this applet, you can clear the sketch by right-clicking it (or by command-clicking on a Macintosh). Try clicking and dragging on the applet:

The source code for this simple sketching program is in the file TrivialSketch.java. The applet ShapeDraw from Section 4.3 uses dragging in a more substantial way. The source code for that applet is in the sample file ShapeDraw.java.


Keyboard and Focus Events

Events don't start out as Java objects. Ultimately, events are caused by user actions that are detected by the computer's operating system. When the user moves the mouse or presses a key, the system has to decide what to do with the information about the user's action. For a mouse action, the information usually goes to the visual GUI interface element that contains the mouse cursor. If that interface element happens to be a Java component, then the information is converted into a Java MouseEvent object, which is in turn sent through Java's event-handling process. But what about keyboard events? When the user presses a key, where does that information go?

GUIs use the idea of input focus to determine where information about keyboard events should be sent. At a given time, exactly one interface element on the screen has the input focus, and that is where all keyboard events are sent. If the interface element happens to be a Java component, then the information about the keyboard event becomes a Java object of type KeyEvent.

It's a good idea to give the user some visual feedback about which component has the input focus. For example, if the component is the typing area of a word-processor, the feedback is usually in the form of a blinking text cursor. Another common visual clue is to draw a brightly colored border around the edge of a component when it has the input focus, as I do in the sample applet later on this page.

A component that wants to have the input focus can call the method requestFocus(), which is defined in the Component class. Calling this method does not absolutely guarantee that the component will actually get the input focus. Several components might request the focus; only one will get it. This method should only be used in certain circumstances in any case, since it can be a rude surprise to the user to have the focus suddenly pulled away from a component that the user is working with. In general, the user can choose to give the focus to a component by clicking on that component with the mouse. And pressing the tab key will often move the focus from one component to another.

Some components do not automatically receive the input focus when the user clicks on them. To solve this problem, a program has to register a mouse listener with the component to detect user clicks. In response to user clicks, the mousePressed() method should call requestFocus() for the component. Unfortunately, which types of components require such treatment varies from platform to platform. In Sun Microsystem's implementation of Java, for example, applet objects and canvas objects must be treated in this way. So if you create a subclass of Applet or Canvas that is supposed to be able to respond to keyboard events, you should be sure to set up a mouse listener for your class and call requestFocus() in the mousePressed() method. This is done in the sample applet example given below.

If a component is to change its appearance when it has the input focus, it needs some way to know when it has the focus. In Java, objects are notified about changes of input focus by events of type FocusEvent. An object that wants to be notified of changes in focus can implement the FocusListener interface. This interface declares two methods:

         public void focusGained(FocusEvent evt);
         public void focusLost(FocusEvent evt);

Furthermore, the addFocusListener() method must be used to set up a listener for the focus events. When a component gets the input focus, it calls the focusGained() method of any object that had been registered with that component as a FocusListener. When it loses the focus, it calls the listener's focusLost() method. Often, it is the component itself that listens for focus events.

The following applet has a boolean instance variable, focussed, which is set to true when the applet gains the focus and to false when it loses the focus. In the paint() method, when focussed is true, a cyan-colored border is drawn around the applet. When it is false, a message is displayed advising the user to click on the applet to activate it. When the applet has the input focus, you can use the arrow keys to move the colored square up, down, left, and right. You can also change the color of the square to red, green, blue, or black by hitting the R, G, B, and K keys. Try it:

In this example, the applet implements the FocusListener interface. The init() method of the applet includes the command "addFocusListener(this);" to arrange for the applet itself to be notified when it gains or loses focus. The focusGained() and focusLost() methods say simply

         public void focusGained(FocusEvent evt) {
               // the applet now has the input focus
            focussed = true;
            repaint();  // redraw with cyan border
         }
   
         public void focusLost(FocusEvent evt) {
               // the applet has now lost the input focus
            focussed = false;
            repaint();  // redraw without cyan border
         }

This applet also handles events that are generated when the user manipulates the keyboard. These events belong to the class KeyEvent. An object that wants to be notified of KeyEvents must implement the KeyListener interface, and it must be registered with the component in which it is interested with that component's addActionListener() method. The KeyListener interface defines three methods:

          public void keyPressed(KeyEvent evt);
          public void keyReleased(KeyEvent evt);
          public void keyTyped(KeyEvent evt);

The keyTyped() method is called when an actual character has been typed. The character can be determined by calling evt.getKeyChar(), which returns a value of type char. The other two methods, keyPressed() and keyReleased() are called whenever a key is pressed or released, including special keys like the shift key and arrow keys. The key can be determined by calling evt.getKeyCode(). This method returns an int value, which is a code for the key. The possible values of this code are given by constants in the KeyEvent class. For example, the four arrow keys correspond to the codes KeyEvent.VK_UP, KeyEvent.VK_DOWN, KeyEvent.VK_LEFT, and KeyEvent.VK_RIGHT, and the code for the shift key is KeyEvent.VK_SHIFT. (The "VK" here stands for "Virtual Keyboard.")

In the sample applet, I use the keyTyped() method to respond when the user hits the R, G, B, or K key:

         public void keyTyped(KeyEvent evt) {
          
            char ch = evt.getKeyChar();  // the character that was typed

            if (ch == 'B' || ch == 'b') {
               squareColor = Color.blue;
               repaint();
            }
            else if (ch == 'G' || ch == 'g') {
               squareColor = Color.green;
               repaint();
            }
            else if (ch == 'R' || ch == 'r') {
               squareColor = Color.red;
               repaint();
            }
            else if (ch == 'K' || ch == 'k') {
               squareColor = Color.black;
               repaint();
            }
   
         }  // end keyTyped()
         

and I use keyPressed() to respond when the user hits one of the arrow keys:

         public void keyPressed(KeyEvent evt) { 
                
            int key = evt.getKeyCode();  // keyboard code for the key that was pressed
            
            if (key == KeyEvent.VK_LEFT) {
               squareLeft -= 8;
               if (squareLeft < 0)
                  squareLeft = 0;
               repaint();
            }
            else if (key == KeyEvent.VK_RIGHT) {
               squareLeft += 8;
               if (squareLeft > width - 6 - squareSize)
                  squareLeft = width - 6 - squareSize;
               repaint();
            }
            else if (key == KeyEvent.VK_UP) {
               squareTop -= 8;
               if (squareTop < 0)
                  squareTop = 0;
               repaint();
            }
            else if (key == KeyEvent.VK_DOWN) {
               squareTop += 8;
               if (squareTop > height - 6 - squareSize)
                  squareTop = height - 6 -squareSize;
               repaint();
            }

         }  // end keyPressed()

Finally, the keyReleased() method, which is required to complete the KeyListener interface, is defined to be empty:

         public void keyReleased(KeyEvent evt) { 
            // empty method, required by the KeyListener Interface
         }

The complete source code for this applet is in the file KeyboardAndFocusDemo.java.


[ Next Section | Previous Section | Chapter Index | Main Index ]