Low-Level Event Types

   


In the sections that follow, we discuss in more detail the events that are not linked to specific user interface components, in particular, events related to keystrokes and mouse activity. You can find a detailed discussion of semantic events generated by user interface components in the next chapter.

Keyboard Events

When the user pushes a key, a KeyEvent with ID KEY_PRESSED is generated. When the user releases the key, a KEY_RELEASED KeyEvent is triggered. You trap these events in the keyPressed and keyReleased methods of any class that implements the KeyListener interface. Use these methods to trap raw keystrokes. A third method, keyTyped, combines the two: it reports on the characters that were generated by the user's keystrokes.

The best way to see what happens is with an example. But before we can do that, we have to add a little more terminology. Java makes a distinction between characters and virtual key codes. Virtual key codes are indicated with a prefix of VK_, such as VK_A or VK_SHIFT. Virtual key codes correspond to keys on the keyboard. For example, VK_A denotes the key marked A. There is no separate lowercase virtual key code the keyboard does not have separate lowercase keys.

NOTE

Virtual key codes are related to the "scan codes" that the keyboard sends to the computer whenever a physical key is pressed or released.


So, suppose that the user types an uppercase "A" in the usual way, by pressing the SHIFT key along with the A key. Java reports five events in response to this user action. Here are the actions and the associated events:

1.

Pressed the SHIFT key (keyPressed called for VK_SHIFT)

2.

Pressed the A key (keyPressed called for VK_A)

3.

Typed "A" (keyTyped called for an "A")

4.

Released the A key (keyReleased called for VK_A)

5.

Released the SHIFT key (keyReleased called for VK_SHIFT)

On the other hand, if the user typed a lowercase "a" by simply pressing the A key, then only three events occur:

1.

Pressed the A key (keyPressed called for VK_A)

2.

Typed "a" (keyTyped called for an "a")

3.

Released the A key (keyReleased called for VK_A)

Thus, the keyTyped procedure reports the character that was typed ("A" or "a"), whereas the keyPressed and keyReleased methods report on the actual keys that the user pressed.

To work with the keyPressed and keyReleased methods, you should first check the key code.

 public void keyPressed(KeyEvent event) {    int keyCode = event.getKeyCode();    . . . } 

The key code will equal one of the following (reasonably mnemonic) constants. They are defined in the KeyEvent class.


VK_A . . . VK_Z
VK_0 . . . VK_9
VK_COMMA, VK_PERIOD, VK_SLASH, VK_SEMICOLON, VK_EQUALS
VK_OPEN_BRACKET, VK_BACK_SLASH, VK_CLOSE_BRACKET
VK_BACK_QUOTE, VK_QUOTE
VK_GREATER, VK_LESS, VK_UNDERSCORE, VK_MINUS
VK_AMPERSAND, VK_ASTERISK, VK_AT, VK_BRACELEFT, VK_BRACERIGHT
VK_LEFT_PARENTHESIS, VK_RIGHT_PARENTHESIS
VK_CIRCUMFLEX, VK_COLON, VK_NUMBER_SIGN, VK_QUOTEDBL
VK_EXCLAMATION_MARK, VK_INVERTED_EXCLAMATION_MARK
VK_DEAD_ABOVEDOT, VK_DEAD_ABOVERING, VK_DEAD_ACUTE
VK_DEAD_BREVE
VK_DEAD_CARON, VK_DEAD_CEDILLA, VK_DEAD_CIRCUMFLEX
VK_DEAD_DIAERESIS
VK_DEAD_DOUBLEACUTE, VK_DEAD_GRAVE, VK_DEAD_IOTA, VK_DEAD_MACRON
VK_DEAD_OGONEK, VK_DEAD_SEMIVOICED_SOUND, VK_DEAD_TILDE VK_DEAD_VOICED_SOUND
VK_DOLLAR, VK_EURO_SIGN
VK_SPACE, VK_ENTER, VK_BACK_SPACE, VK_TAB, VK_ESCAPE
VK_SHIFT, VK_CONTROL, VK_ALT, VK_ALT_GRAPH, VK_META
VK_NUM_LOCK, VK_SCROLL_LOCK, VK_CAPS_LOCK
VK_PAUSE, VK_PRINTSCREEN
VK_PAGE_UP, VK_PAGE_DOWN, VK_END, VK_HOME, VK_LEFT, VK_UP, VK_RIGHT, VK_DOWN
VK_F1 . . .VK_F24
VK_NUMPAD0 . . . VK_NUMPAD9
VK_KP_DOWN, VK_KP_LEFT, VK_KP_RIGHT, VK_KP_UP
VK_MULTIPLY, VK_ADD, VK_SEPARATER [sic], VK_SUBTRACT, VK_DECIMAL, VK_DIVIDE
VK_DELETE, VK_INSERT
VK_HELP, VK_CANCEL, VK_CLEAR, VK_FINAL
VK_CONVERT, VK_NONCONVERT, VK_ACCEPT, VK_MODECHANGE
VK_AGAIN, VK_ALPHANUMERIC, VK_CODE_INPUT, VK_COMPOSE, VK_PROPS
VK_STOP
VK_ALL_CANDIDATES, VK_PREVIOUS_CANDIDATE
VK_COPY, VK_CUT, VK_PASTE, VK_UNDO
VK_FULL_WIDTH, VK_HALF_WIDTH
VK_HIRAGANA, VK_KATAKANA, VK_ROMAN_CHARACTERS
VK_KANA, VK_KANJI
VK_JAPANESE_HIRAGANA, VK_JAPANESE_KATAKANA, VK_JAPANESE_ROMAN
VK_WINDOWS, VK_CONTEXT_MENU
VK_UNDEFINED

To find the current state of the SHIFT, CONTROL, ALT, and META keys, you can, of course, track the VK_SHIFT, VK_CONTROL, VK_ALT, and VK_META key presses, but that is tedious. Instead, simply use the isShiftDown, isControlDown, isAltDown, and isMetaDown methods. (Sun and Macintosh keyboards have a special META key. On a Sun keyboard, the key is marked a diamond. On a Macintosh, the key is marked with an apple and a cloverleaf.)

For example, the following code tests whether the user presses SHIFT + RIGHT ARROW:

 public void keyPressed(KeyEvent event) {    int keyCode = event.getKeyCode();    if (keyCode == KeyEvent.VK_RIGHT && event.isShiftDown())    {       . . .    } } 

In the keyTyped method, you call the getKeyChar method to obtain the actual character that was typed.

NOTE

Not all keystrokes result in a call to keyTyped. Only those keystrokes that generate a Unicode character can be captured in the keyTyped method. You use the keyPressed method to check for cursor keys and other command keys.


Example 8-3 shows how to handle keystrokes. The program (shown in Figure 8-7) is a simple implementation of the Etch-A-Sketch toy.

Figure 8-7. A sketch program


You move a pen up, down, left, and right with the cursor keys. If you hold down the SHIFT key, the pen moves by a larger increment. Or, if you are experienced in using the vi editor, you can bypass the cursor keys and use the lowercase h, j, k, and l keys to move the pen. The uppercase H, J, K, and L move the pen by a larger increment. We trap the cursor keys in the keyPressed method and the characters in the keyTyped method.

There is one technicality: Normally, a panel does not receive any key events. To override this default, we call the setFocusable method. We discuss the concept of keyboard focus later in this chapter.

Example 8-3. Sketch.java
   1. import java.awt.*;   2. import java.awt.geom.*;   3. import java.util.*;   4. import java.awt.event.*;   5. import javax.swing.*;   6.   7. public class Sketch   8. {   9.    public static void main(String[] args)  10.    {  11.       SketchFrame frame = new SketchFrame();  12.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  13.       frame.setVisible(true);  14.    }  15. }  16.  17. /**  18.    A frame with a panel for sketching a figure  19. */  20. class SketchFrame extends JFrame  21. {  22.    public SketchFrame()  23.    {  24.       setTitle("Sketch");  25.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  26.  27.       // add panel to frame  28.  29.       SketchPanel panel = new SketchPanel();  30.       add(panel);  31.    }  32.  33.    public static final int DEFAULT_WIDTH = 300;  34.    public static final int DEFAULT_HEIGHT = 200;  35. }  36.  37. /**  38.    A panel for sketching with the keyboard.  39. */  40. class SketchPanel extends JPanel  41. {  42.    public SketchPanel()  43.    {  44.       last = new Point2D.Double(100, 100);  45.       lines = new ArrayList<Line2D>();  46.       KeyHandler listener = new KeyHandler();  47.       addKeyListener(listener);  48.       setFocusable(true);  49.    }  50.  51.    /**  52.       Add a new line segment to the sketch.  53.       @param dx the movement in x direction  54.       @param dy the movement in y direction  55.    */  56.    public void add(int dx, int dy)  57.    {  58.       // compute new end point  59.       Point2D end = new Point2D.Double(last.getX() + dx, last.getY() + dy);  60.  61.       // add line segment  62.       Line2D line = new Line2D.Double(last, end);  63.       lines.add(line);  64.       repaint();  65.  66.       // remember new end point  67.       last = end;  68.    }  69.  70.    public void paintComponent(Graphics g)  71.    {  72.       super.paintComponent(g);  73.       Graphics2D g2 = (Graphics2D) g;  74.  75.       // draw all lines  76.       for (Line2D l : lines)  77.          g2.draw(l);  78.    }  79.  80.    private Point2D last;  81.    private ArrayList<Line2D> lines;  82.  83.    private static final int SMALL_INCREMENT = 1;  84.    private static final int LARGE_INCREMENT = 5;  85.  86.    private class KeyHandler implements KeyListener  87.    {  88.       public void keyPressed(KeyEvent event)  89.       {  90.          int keyCode = event.getKeyCode();  91.  92.          // set distance  93.          int d;  94.          if (event.isShiftDown())  95.             d = LARGE_INCREMENT;  96.          else  97.             d = SMALL_INCREMENT;  98.  99.          // add line segment 100.          if (keyCode == KeyEvent.VK_LEFT) add(-d, 0); 101.          else if (keyCode == KeyEvent.VK_RIGHT) add(d, 0); 102.          else if (keyCode == KeyEvent.VK_UP) add(0, -d); 103.          else if (keyCode == KeyEvent.VK_DOWN) add(0, d); 104.       } 105. 106.       public void keyReleased(KeyEvent event) {} 107. 108.       public void keyTyped(KeyEvent event) 109.       { 110.          char keyChar = event.getKeyChar(); 111. 112.          // set distance 113.          int d; 114.          if (Character.isUpperCase(keyChar)) 115.          { 116.             d = LARGE_INCREMENT; 117.             keyChar = Character.toLowerCase(keyChar); 118.          } 119.          else 120.             d = SMALL_INCREMENT; 121. 122.          // add line segment 123.          if (keyChar == 'h') add(-d, 0); 124.          else if (keyChar == 'l') add(d, 0); 125.          else if (keyChar == 'k') add(0, -d); 126.          else if (keyChar == 'j') add(0, d); 127.       } 128.    } 129. } 


 java.awt.event.KeyEvent 1.1 

  • char getKeyChar()

    returns the character that the user typed.

  • int getKeyCode()

    returns the virtual key code of this key event.

  • boolean isActionKey()

    returns TRue if the key in this event is an "action" key. The following keys are action keys: HOME, END, PAGE UP, PAGE DOWN, UP, DOWN, LEFT, RIGHT, F1 ... F24, PRINT SCREEN, SCROLL LOCK, CAPS LOCK, NUM LOCK, PAUSE, INSERT, DELETE, ENTER, BACKSPACE, DELETE, and TAB.

  • static String getKeyText(int keyCode)

    returns a string describing the key code. For example, getKeyText(KeyEvent.VK_END) is the string "End".

  • static String getKeyModifiersText(int modifiers)

    returns a string describing the modifier keys, such as SHIFT or CTRL + SHIFT.

    Parameters:

    modifiers

    The modifier state, as reported by getModifiers



 java.awt.event.InputEvent 1.1 

  • int getModifiers()

    returns an integer whose bits describe the state of the modifiers SHIFT, CONTROL, ALT, and META. This method applies to both keyboard and mouse events. To see if a bit is set, test the return value against one of the bit masks SHIFT_MASK, CTRL_MASK, ALT_MASK, ALT_GRAPH_MASK, META_MASK, or use one of the following methods.

  • boolean isShiftDown()

  • boolean isControlDown()

  • boolean isAltDown()

  • boolean isAltGraphDown() 1.2

  • boolean isMetaDown()

    return TRue if the modifier key was held down when this event was generated.

Mouse Events

You do not need to handle mouse events explicitly if you just want the user to be able to click on a button or menu. These mouse operations are handled internally by the various components in the user interface and then translated into the appropriate semantic event. However, if you want to enable the user to draw with the mouse, you will need to trap mouse move, click, and drag events.

In this section, we show you a simple graphics editor application that allows the user to place, move, and erase squares on a canvas (see Figure 8-8).

Figure 8-8. A mouse test program


When the user clicks a mouse button, three listener methods are called: mousePressed when the mouse is first pressed, mouseReleased when the mouse is released, and, finally, mouseClicked. If you are only interested in complete clicks, you can ignore the first two methods. By using the getX and getY methods on the MouseEvent argument, you can obtain the x- and y-coordinates of the mouse pointer when the mouse was clicked. To distinguish between single, double, and triple (!) clicks, use the getClickCount method.

Some user interface designers inflict mouse click and keyboard modifier combinations, such as CONTROL + SHIFT + CLICK, on their users. We find this practice reprehensible, but if you disagree, you will find that checking for mouse buttons and keyboard modifiers is a mess. In the original API, two of the button masks equal two keyboard modifier masks, namely:

 BUTTON2_MASK == ALT_MASK BUTTON3_MASK == META_MASK 

This was done so that users with a one-button mouse could simulate the other mouse buttons by holding down modifier keys instead. However, as of JDK 1.4, a different approach is recommended. There are now masks

 BUTTON1_DOWN_MASK BUTTON2_DOWN_MASK BUTTON3_DOWN_MASK SHIFT_DOWN_MASK CTRL_DOWN_MASK ALT_DOWN_MASK ALT_GRAPH_DOWN_MASK META_DOWN_MASK 

The getModifiersEx method accurately reports the mouse buttons and keyboard modifiers of a mouse event.

Note that BUTTON3_DOWN_MASK tests for the right (nonprimary) mouse button under Windows. For example, you can use code like this to detect whether the right mouse button is down:

 if ((event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0)    . . . // code for right click 

In our sample program, we supply both a mousePressed and a mouseClicked method. When you click onto a pixel that is not inside any of the squares that have been drawn, a new square is added. We implemented this in the mousePressed method so that the user receives immediate feedback and does not have to wait until the mouse button is released. When a user double-clicks inside an existing square, it is erased. We implemented this in the mouseClicked method because we need the click count.

 public void mousePressed(MouseEvent event) {    current = find(event.getPoint());    if (current == null) // not inside a square       add(event.getPoint()); } public void mouseClicked(MouseEvent event) {    current = find(event.getPoint());    if (current != null && event.getClickCount() >= 2)       remove(current); } 

As the mouse moves over a window, the window receives a steady stream of mouse movement events. These are ignored by most applications. However, our test application traps the events to change the cursor to a different shape (a cross hair) when it is over a square. This is done with the getPredefinedCursor method of the Cursor class. Table 8-2 lists the constants to use with this method along with what the cursors look like under Windows. (Note that several of these cursors look the same, but you should check how they look on your platform.)

Table 8-2. Sample Cursor Shapes

Icon

Constant

DEFAULT_CURSOR

CROSSHAIR_CURSOR

HAND_CURSOR

MOVE_CURSOR

TEXT_CURSOR

WAIT_CURSOR

N_RESIZE_CURSOR

NE_RESIZE_CURSOR

E_RESIZE_CURSOR

SE_RESIZE_CURSOR

S_RESIZE_CURSOR

SW_RESIZE_CURSOR

W_RESIZE_CURSOR

NW_RESIZE_CURSOR


TIP

You can find cursor images in the jre/lib/images/cursors directory. The file cursors.properties defines the cursor "hot spots." This is the point that the user associates with the pointing action of the cursor. For example, if the cursor has the shape of a magnifying glass, the hot spot would be the center of the lens.


Here is the mouseMoved method of the MouseMotionListener in our example program:

 public void mouseMoved(MouseEvent event) {    if (find(event.getPoint()) == null)       setCursor(Cursor.getDefaultCursor());    else       setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); } 

NOTE

You can also define your own cursor types through the use of the createCustomCursor method in the Toolkit class:

 Toolkit tk = Toolkit.getDefaultToolkit(); Image img = tk.getImage("dynamite.gif"); Cursor dynamiteCursor = tk.createCustomCursor(img, new Point(10, 10), "dynamite stick"); 

The first parameter of the createCustomCursor points to the cursor image. The second parameter gives the offset of the "hot spot" of the cursor. The third parameter is a string that describes the cursor. This string can be used for accessibility support, for example, to read the cursor shape to a user who is visually impaired or who simply is not facing the screen.


If the user presses a mouse button while the mouse is in motion, mouseDragged calls are generated instead of mouseMoved calls. Our test application lets a user drag the square under the cursor. We simply update the currently dragged rectangle to be centered under the mouse position. Then, we repaint the canvas to show the new mouse position.

 public void mouseDragged(MouseEvent event) {    if (current != null)    {       int x = event.getX();       int y = event.getY();       current.setFrame(          x - SIDELENGTH / 2,          y - SIDELENGTH / 2,          SIDELENGTH,          SIDELENGTH);       repaint(); } 

NOTE

The mouseMoved method is only called as long as the mouse stays inside the component. However, the mouseDragged method keeps getting called even when the mouse is being dragged outside the component.


There are two other mouse event methods: mouseEntered and mouseExited. These methods are called when the mouse enters or exits a component.

Finally, we explain how to listen to mouse events. Mouse clicks are reported through the mouseClicked procedure, which is part of the MouseListener interface. Because many applications are interested only in mouse clicks and not in mouse moves and because mouse move events occur so frequently, the mouse move and drag events are defined in a separate interface called MouseMotionListener.

In our program we are interested in both types of mouse events. We define two inner classes: MouseHandler and MouseMotionHandler. The MouseHandler class extends the MouseAdapter class because it defines only two of the five MouseListener methods. The MouseMotionHandler implements the MouseMotionListener and defines both methods of that interface.

Example 8-4 is the program listing.

Example 8-4. MouseTest.java
   1. import java.awt.*;   2. import java.awt.event.*;   3. import java.util.*;   4. import java.awt.geom.*;   5. import javax.swing.*;   6.   7. public class MouseTest   8. {   9.    public static void main(String[] args)  10.    {  11.       MouseFrame frame = new MouseFrame();  12.       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);  13.       frame.setVisible(true);  14.    }  15. }  16.  17. /**  18.    A frame containing a panel for testing mouse operations  19. */  20. class MouseFrame extends JFrame  21. {  22.    public MouseFrame()  23.    {  24.       setTitle("MouseTest");  25.       setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);  26.  27.       // add panel to frame  28.  29.       MousePanel panel = new MousePanel();  30.       add(panel);  31.    }  32.  33.    public static final int DEFAULT_WIDTH = 300;  34.    public static final int DEFAULT_HEIGHT = 200;  35. }  36.  37. /**  38.    A panel with mouse operations for adding and removing squares.  39. */  40. class MousePanel extends JPanel  41. {  42.    public MousePanel()  43.    {  44.       squares = new ArrayList<Rectangle2D>();  45.       current = null;  46.  47.       addMouseListener(new MouseHandler());  48.       addMouseMotionListener(new MouseMotionHandler());  49.    }  50.  51.    public void paintComponent(Graphics g)  52.    {  53.       super.paintComponent(g);  54.       Graphics2D g2 = (Graphics2D) g;  55.  56.       // draw all squares  57.       for (Rectangle2D r : squares)  58.          g2.draw(r);  59.    }  60.  61.    /**  62.       Finds the first square containing a point.  63.       @param p a point  64.       @return the first square that contains p  65.    */  66.    public Rectangle2D find(Point2D p)  67.    {  68.       for (Rectangle2D r : squares)  69.       {  70.          if (r.contains(p)) return r;  71.       }  72.       return null;  73.    }  74.  75.    /**  76.       Adds a square to the collection.  77.       @param p the center of the square  78.    */  79.    public void add(Point2D p)  80.    {  81.       double x = p.getX();  82.       double y = p.getY();  83.  84.       current = new Rectangle2D.Double(  85.          x - SIDELENGTH / 2,  86.          y - SIDELENGTH / 2,  87.          SIDELENGTH,  88.          SIDELENGTH);  89.       squares.add(current);  90.       repaint();  91.    }  92.  93.    /**  94.       Removes a square from the collection.  95.       @param s the square to remove  96.    */  97.    public void remove(Rectangle2D s)  98.    {  99.       if (s == null) return; 100.       if (s == current) current = null; 101.       squares.remove(s); 102.       repaint(); 103.    } 104. 105. 106.    private static final int SIDELENGTH = 10; 107.    private ArrayList<Rectangle2D> squares; 108.    private Rectangle2D current; 109.    // the square containing the mouse cursor 110. 111.    private class MouseHandler extends MouseAdapter 112.    { 113.       public void mousePressed(MouseEvent event) 114.       { 115.          // add a new square if the cursor isn't inside a square 116.          current = find(event.getPoint()); 117.          if (current == null) 118.             add(event.getPoint()); 119.       } 120. 121.       public void mouseClicked(MouseEvent event) 122.       { 123.          // remove the current square if double clicked 124.          current = find(event.getPoint()); 125.          if (current != null && event.getClickCount() >= 2) 126.             remove(current); 127.       } 128.    } 129. 130.    private class MouseMotionHandler 131.       implements MouseMotionListener 132.    { 133.       public void mouseMoved(MouseEvent event) 134.       { 135.          // set the mouse cursor to cross hairs if it is inside 136.          // a rectangle 137. 138.          if (find(event.getPoint()) == null) 139.             setCursor(Cursor.getDefaultCursor()); 140.          else 141.             setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR)); 142.       } 143. 144.       public void mouseDragged(MouseEvent event) 145.       { 146.          if (current != null) 147.          { 148.             int x = event.getX(); 149.             int y = event.getY(); 150. 151.             // drag the current rectangle to center it at (x, y) 152.             current.setFrame( 153.                x - SIDELENGTH / 2, 154.                y - SIDELENGTH / 2, 155.                SIDELENGTH, 156.                SIDELENGTH); 157.             repaint(); 158.          } 159.       } 160.    } 161. } 


 java.awt.event.MouseEvent 1.1 

  • int getX()

  • int getY()

  • Point getPoint()

    return the x- (horizontal) and y- (vertical) coordinate, or point where the event happened, measured from the top-left corner of the component that is the event source.

  • void translatePoint(int x, int y)

    translates the coordinates of the event by moving x units horizontally and y units vertically.

  • int getClickCount()

    returns the number of consecutive mouse clicks associated with this event. (The time interval for what constitutes "consecutive" is system dependent.)


 java.awt.event.InputEvent 1.1 

  • int getModifiersEx() 1.4

    returns the extended or "down" modifiers for this event. Use the following mask values to test the returned value:

     BUTTON1_DOWN_MASK BUTTON2_DOWN_MASK BUTTON3_DOWN_MASK SHIFT_DOWN_MASK CTRL_DOWN_MASK ALT_DOWN_MASK ALT_GRAPH_DOWN_MASK META_DOWN_MASK 

  • static String getModifiersExText(int modifiers) 1.4

    returns a string such as "Shift+Button1" describing the extended or "down" modifiers in the given flag set.


 java.awt.Toolkit 1.0 

  • public Cursor createCustomCursor(Image image, Point hotSpot, String name) 1.2

    creates a new custom cursor object.

    Parameters:

    image

    The image to display when the cursor is active

     

    hotSpot

    The cursor's hot spot (such as the tip of an arrow or the center of cross hairs)

     

    name

    A description of the cursor, to support special accessibility environments



 java.awt.Component 1.0 

  • public void setCursor(Cursor cursor) 1.1

    sets the cursor image to the specified cursor.

Focus Events

When you use a mouse, you can point to any object on the screen. But when you type, your keystrokes must go to a specific screen object. The window manager (such as Windows or X Windows) directs all keystrokes to the active window. Often, the active window is distinguished by a highlighted title bar. Only one window can be active at any one time.

Now suppose the active window is controlled by a Java program. The Java window receives the keystrokes, and it in turn directs them toward a particular component. That component is said to have focus. Just like the active window is usually distinguished in some way, most Swing components give a visual cue if they currently have focus. A text field has a blinking caret, a button has a rectangle around the label, and so on. When a text field has focus, you can enter text into the text field. When a button has focus, you can "click" it by pressing the space bar.

Only one component in a window can have focus. A component loses focus if the user clicks on another component, which then gains focus. The user can also use the TAB key to give focus to each component in turn. This traverses all components that are able to receive input focus. By default, Swing components are traversed from left to right, then top to bottom, as they are laid out in the container. You can change the focus traversal order; see the next chapter for more on this subject.

NOTE

Unfortunately, focus handling in older versions of the JDK had quite a few problems, with over 100 separate bugs reported. The reasons for focus flakiness were twofold. Component focus interacts with window focus, which is the responsibility of the windowing system. Therefore, some focus behavior showed platform-dependent variations. Moreover, the implementation code had apparently mushroomed out of control, particularly with the addition of an unsatisfactory focus traversal architecture in JDK 1.3.

Winston Churchill once said "The Americans will always do the right thing... after they've exhausted all the alternatives." Apparently, the same is true for the Java team, and they decided to do the right thing in JDK 1.4. They completely reimplemented the focus handling code, and they provided a complete description of the expected behavior, including an explanation of unavoidable platform dependencies.

You can find the focus specification at http://java.sun.com/j2se/1.4/docs/api/java/awt/doc-files/FocusSpec.html.


Fortunately, most application programmers don't need to worry too much about focus handling. Before JDK 1.4, a common use for trapping component focus events was error checking or data validation. Suppose you have a text field that contains a credit card number. When the user is done editing the field and moves to another field, you trap the lost focus event. If the credit card format was not formatted properly, you can display an error message and give the focus back to the credit card field. However, JDK 1.4 has robust validation mechanisms that are easier to program. We discuss validation in Chapter 9.

Some components, such as labels or panels, do not get focus by default because it is assumed that they are just used for decoration or grouping. You need to override this default if you implement a drawing program with panels that paint something in reaction to keystrokes. As of JDK 1.4, you can simply call

 panel.setFocusable(true); 

NOTE

In older versions of the JDK, you had to override the isFocusTraversable method of the component to achieve the same effect. However, the old focus implementation had separate concepts for the ability to gain focus and participation in focus traversal. That distinction led to confusing behavior and has now been removed. The isFocusTraversable method has been deprecated.


In the remainder of this section, we discuss focus event details that you can safely skip until you have a special need that requires fine-grained control of focus handling.

In JDK 1.4, you can easily find out

  • The focus owner, that is, the component that has focus;

  • The focused window, that is, the window that contains the focus owner;

  • The active window, that is, the frame or dialog that contains the focus owner.

The focused window is usually the same as the active window. You get a different result only when the focus owner is contained in a top-level window with no frame decorations, such as a pop-up menu.

To obtain that information, first get the keyboard focus manager:

 KeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); 

Then call

 Component owner = manager.getFocusOwner(); Window focused = manager.getFocusedWindow(); Window active = manager.getActiveWindow(); // a frame or dialog 

For notification of focus changes, you need to install focus listeners into components or windows. A component focus listener must implement the FocusListener interface with two methods, focusGained and focusLost. These methods are triggered when the component gains or loses the focus. Each of these methods has a FocusEvent parameter. There are several useful methods for this class. The getComponent method reports the component that gained or lost the focus, and the isTemporary method returns true if the focus change was temporary. A temporary focus change occurs when a component loses control temporarily but will automatically get it back. This happens, for example, when the user selects a different active window. As soon as the user selects the current window again, the same component regains focus.

JDK 1.4 introduces the delivery of window focus events. You add a WindowFocusListener to a window and implement the windowGainedFocus and windowLostFocus methods.

As of JDK 1.4, you can find out the "opposite" component or window when focus is transferred. When a component or window has lost focus, the opposite is the component or window that gains focus. Conversely, when a component or window gains focus, then its opposite is the one that lost focus. The getOppositeComponent method of the FocusEvent class reports the opposite component, and the getOppositeWindow of the WindowEvent class reports the opposite window.

You can programmatically move the focus to another component by calling the requestFocus method of the Component class. However, the behavior is intrinsically platform dependent if the component is not contained in the currently focused window. To enable programmers to develop platform-independent code, JDK 1.4 adds a method requestFocusInWindow to the Component class. That method succeeds only if the component is contained in the focused window.

NOTE

You should not assume that your component has focus if requestFocus or requestFocusInWindow returns true. Wait for the FOCUS_GAINED event to be delivered to be sure.


NOTE

Some programmers are confused about the FOCUS_LOST event and try to stop another component from gaining focus by requesting the focus in the focusLost handler. However, at that time, the focus is already lost. If you must trap the focus in a particular component, install a "vetoable change listener" in the KeyboardFocusManager and veto the focusOwner property. See Chapter 8 of Volume 2 for details on property vetoes.



 java.awt.Component 1.0 

  • void requestFocus()

    requests that this component gets the focus.

  • boolean requestFocusInWindow() 1.4

    requests that this component gets the focus. Returns false if this component is not contained in the focused window or if the request cannot be fulfilled for another reason. Returns TRue if it is likely that the request will be fulfilled.

  • void setFocusable(boolean b) 1.4

  • boolean isFocusable() 1.4

    set or get the "focusable" state of this component. If b is true, then this component can gain focus.

  • boolean isFocusOwner() 1.4

    returns true if this component currently owns the focus.


 java.awt.KeyboardFocusManager 1.4 

  • static KeyboardFocusManager getCurrentKeyboardFocusManager()

    gets the current focus manager.

  • Component getFocusOwner()

    gets the component that owns the focus, or null if this focus manager does not manage the component that has focus.

  • Window getFocusedWindow()

    gets the window that contains the component that owns the focus, or null if this focus manager does not manage the component that has focus.

  • Window getActiveWindow()

    gets the dialog or frame that contains the focused window, or null if this focus manager does not manage the focused window.


 java.awt.Window() 1.0 

  • boolean isFocused() 1.4

    returns true if this window is currently the focused window.

  • boolean isActive() 1.4

    returns true if this frame or dialog is currently the active window. The title bars of active frames and dialogs are usually marked by the window manager.


 java.awt.event.FocusEvent 1.1 

  • Component getOppositeComponent() 1.4

    returns the component that lost focus in the focusGained handler, or the component that gained focus in the focusLost handler.


 java.awt.event.WindowEvent 1.4 

  • Window getOppositeWindow() 1.4

    returns the window that lost focus in the windowGainedFocus handler, the window that gained focus in the windowLostFocus handler, the window that was deactivated in the windowActivated handler, or the window that was activated in the windowDeactivated handler.


 java.awt.event.WindowFocusListener 1.4 

  • void windowGainedFocus(WindowEvent event)

    is called when the event source window gained focus.

  • void windowLostFocus(WindowEvent event)

    is called when the event source window lost focus.


       
    top



    Core Java 2 Volume I - Fundamentals
    Core Java(TM) 2, Volume I--Fundamentals (7th Edition) (Core Series) (Core Series)
    ISBN: 0131482025
    EAN: 2147483647
    Year: 2003
    Pages: 132

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net