Low-Level Event Types

   

Core Java™ 2: Volume I - Fundamentals
By Cay S. Horstmann, Gary Cornell
Table of Contents
Chapter 8.  Event Handling


In the sections that follow, we will 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 key_Pressed KeyEvent is generated. When the user releases the key, a key_Release 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.

graphics/notes_icon.gif

Virtual key codes are similar (and related to) the scan codes of a PC keyboard.

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 there are only three events:

  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 want to 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_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.

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.

graphics/notes_icon.gif

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 need to 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

graphics/08fig07.gif

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 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.show();  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.       Container contentPane = getContentPane();  31.       contentPane.add(panel);  32.    }  33.  34.    public static final int DEFAULT_WIDTH = 300;  35.    public static final int DEFAULT_HEIGHT = 200;  36. }  37.  38. /**  39.  A panel for sketching with the keyboard.  40. */  41. class SketchPanel extends JPanel  42. {  43.    public SketchPanel()  44.    {  45.       last = new Point2D.Double(100, 100);  46.       lines = new ArrayList();  47.       KeyHandler listener = new KeyHandler();  48.       addKeyListener(listener);  49.       setFocusable(true);  50.    }  51.  52.    /**  53.       Add a new line segment to the sketch.  54.       @param dx the movement in x direction  55.       @param dy the movement in y direction  56.    */  57.    public void add(int dx, int dy)  58.    {  59.       // compute new end point  60.       Point2D end = new Point2D.Double(last.getX() + dx,  61.          last.getY() + dy);  62.  63.       // add line segment  64.       Line2D line = new Line2D.Double(last, end);  65.       lines.add(line);  66.       repaint();  67.  68.       // remember new end point  69.       last = end;  70.    }  71.  72.    public void paintComponent(Graphics g)  73.    {  74.       super.paintComponent(g);  75.       Graphics2D g2 = (Graphics2D)g;  76.  77.       // draw all lines  78.       for (int i = 0; i < lines.size(); i++)  79.          g2.draw((Line2D)lines.get(i));  80.    }  81.  82.    private Point2D last;  83.    private ArrayList lines;  84.  85.    private static final int SMALL_INCREMENT = 1;  86.    private static final int LARGE_INCREMENT = 5;  87.  88.    private class KeyHandler implements KeyListener  89.    {  90.       public void keyPressed(KeyEvent event)  91.       {  92.          int keyCode = event.getKeyCode();  93.  94.          // set distance  95.          int d;  96.          if (event.isShiftDown())  97.             d = LARGE_INCREMENT;  98.          else  99.             d = SMALL_INCREMENT; 100. 101.         // add line segment 102.         if (keyCode == KeyEvent.VK_LEFT) add(-d, 0); 103.         else if (keyCode == KeyEvent.VK_RIGHT) add(d, 0); 104.         else if (keyCode == KeyEvent.VK_UP) add(0, -d); 105.         else if (keyCode == KeyEvent.VK_DOWN) add(0, d); 106.      } 107. 108.      public void keyReleased(KeyEvent event) {} 109. 110.      public void keyTyped(KeyEvent event) 111.      { 112.         char keyChar = event.getKeyChar(); 113. 114.         // set distance 115.         int d; 116.         if (Character.isUpperCase(keyChar)) 117.         { 118.            d = LARGE_INCREMENT; 119.            keyChar = Character.toLowerCase(keyChar); 120.         } 121.         else 122.            d = SMALL_INCREMENT; 123. 124.         // add line segment 125.         if (keyChar == 'h') add(-d, 0); 126.         else if (keyChar == 'l') add(d, 0); 127.         else if (keyChar == 'k') add(0, -d); 128.         else if (keyChar == 'j') add(0, d); 129.      } 130.   } 131.} 

java.awt.event.KeyEvent 1.1

graphics/api_icon.gif
  • 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

graphics/api_icon.gif
  • 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()

    The methods 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 will 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

graphics/08fig08.gif

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. If you want 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 

Presumably 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 SDK 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 if 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

graphics/defaultcursor.gif

DEFAULT_CURSOR

graphics/croshair.gif

CROSSHAIR_CURSOR

graphics/hand.gif

HAND_CURSOR

graphics/move.gif

MOVE_CURSOR

graphics/text.gif

TEXT_CURSOR

graphics/wait.gif

WAIT_CURSOR

graphics/nresize.gif

N_RESIZE_CURSOR

graphics/neresize.gif

NE_RESIZE_CURSOR

graphics/wresize.gif

E_RESIZE_CURSOR

graphics/nwresize.gif

SE_RESIZE_CURSOR

graphics/sresize.gif

S_RESIZE_CURSOR

graphics/swresize.gif

SW_RESIZE_CURSOR

graphics/wresize.gif

W_RESIZE_CURSOR

graphics/seresize.gif

NW_RESIZE_CURSOR

graphics/exclamatory_icon.gif

You can find cursor images in the jre/lib/images/cursors/cursors.properties 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 pointing hand, the hot spot would be the tip of the index finger. 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)); } 

graphics/notes_icon.gif

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 mouseClicked calls. Our test application lets you 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 >= 0)    {       int x = event.getX();       int y = event.getY();       current.setFrame(          x - SIDELENGTH / 2,          y - SIDELENGTH / 2,          SIDELENGTH,          SIDELENGTH);       repaint(); } 

graphics/notes_icon.gif

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 need to 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 only defines 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.show();  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.       Container contentPane = getContentPane();  31.       contentPane.add(panel);  32.    }  33.  34.    public static final int DEFAULT_WIDTH = 300;  35.    public static final int DEFAULT_HEIGHT = 200;  36. }  37.  38. /**  39.    A panel with mouse operations for adding and removing squares.  40. */  41. class MousePanel extends JPanel  42. {  43.    public MousePanel()  44.    {  45.       squares = new ArrayList();  46.       current = null;  47.  48.       addMouseListener(new MouseHandler());  49.       addMouseMotionListener(new MouseMotionHandler());  50.    }  51.  52.    public void paintComponent(Graphics g)  53.    {  54.       super.paintComponent(g);  55.       Graphics2D g2 = (Graphics2D)g;  56.  57.       // draw all squares  58.       for (int i = 0; i < squares.size(); i++)  59.          g2.draw((Rectangle2D)squares.get(i));  60.    }  61.  62.    /**  63.       Finds the first square containing a point.  64.       @param p a point  65.       @return the first square that contains p  66.    */  67.    public Rectangle2D find(Point2D p)  68.    {  69.       for (int i = 0; i < squares.size(); i++)  70.       {  71.          Rectangle2D r = (Rectangle2D)squares.get(i);  72.          if (r.contains(p)) return r;  73.       }  74.  75.       return null;  76.    }  77.  78.    /**  79.       Adds a square to the collection.  80.       @param p the center of the square  81.    */  82.    public void add(Point2D p)  83.    {  84.       double x = p.getX();  85.       double y = p.getY();  86.  87.       current = new Rectangle2D.Double(  88.          x - SIDELENGTH / 2,  89.          y - SIDELENGTH / 2,  90.          SIDELENGTH,  91.          SIDELENGTH);  92.       squares.add(current);  93.       repaint();  94.    }  95.  96.    /**  97.       Removes a square from the collection.  98.       @param s the square to remove  99.    */ 100.    public void remove(Rectangle2D s) 101.    { 102.       if (s == null) return; 103.       if (s == current) current = null; 104.       squares.remove(s); 105.       repaint(); 106.    } 107. 108. 109.    private static final int SIDELENGTH = 10; 110.    private ArrayList squares; 111.    private Rectangle2D current; 112.    // the square containing the mouse cursor 113. 114.    private class MouseHandler extends MouseAdapter 115.    { 116.       public void mousePressed(MouseEvent event) 117.       { 118.          // add a new square if the cursor isn't inside a square 119.          current = find(event.getPoint()); 120.          if (current == null) 121.             add(event.getPoint()); 122.       } 123. 124.       public void mouseClicked(MouseEvent event) 125.       { 126.          // remove the current square if double clicked 127.          current = find(event.getPoint()); 128.          if (current != null && event.getClickCount() >= 2) 129.             remove(current); 130.       } 131.    } 132. 133.    private class MouseMotionHandler 134.       implements MouseMotionListener 135.    { 136.       public void mouseMoved(MouseEvent event) 137.       { 138.          // set the mouse cursor to cross hairs if it is inside 139.          // a rectangle 140. 141.          if (find(event.getPoint()) == null) 142.             setCursor(Cursor.getDefaultCursor()); 143.          else 144.             setCursor(Cursor.getPredefinedCursor 145.                (Cursor.CROSSHAIR_CURSOR)); 146.       } 147. 148.       public void mouseDragged(MouseEvent event) 149.       { 150.          if (current != null) 151.          { 152.             int x = event.getX(); 153.             int y = event.getY(); 154. 155.             // drag the current rectangle to center it at (x, y) 156.             current.setFrame( 157.                x - SIDELENGTH / 2, 158.                y - SIDELENGTH / 2, 159.                SIDELENGTH, 160.                SIDELENGTH); 161.             repaint(); 162.          } 163.       } 164.    } 165. } 

java.awt.event.MouseEvent 1.1

graphics/api_icon.gif
  • int getX()

  • int getY()

  • Point getPoint()

    return the x- (horizontal) and y- (vertical) coordinate, or point where the event happened, using the coordinate system of the 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

graphics/api_icon.gif
  • int getModifiersEx() 1.4

    returns the extended or "down" modifiers for this event. Use the mask values

     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 

    to test the returned value.

  • static int 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

graphics/notes_icon.gif
  • 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

graphics/notes_icon.gif
  • public void setCursor(Cursor cursor) 1.1

    sets the cursor image to one of the predefined cursors specified by the cursor parameter.

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

There is at most one component in a window that has focus. A component can lose focus if the user selects another component, which then gains focus. A component gains focus if the user clicks the mouse inside it. 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 shown in the ambient container. You can change the focus traversal order; see the next chapter for more on this subject.

graphics/notes_icon.gif

Unfortunately, the focus handling in older versions of the SDK had quite a few problems, with over 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 SDK 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 SDK 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. Prior to SDK 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, SDK 1.4 has robust validation mechanisms that are easier to program. We will 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 SDK 1.4, you can simply call

 panel.setFocusable(true); 

graphics/notes_icon.gif

In older versions of the SDK, 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 details about focus events that you can safely skip until you have a special need that requires fine-grained control of focus handling.

In SDK 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 = manageer.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 the focus.

SDK 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 SDK 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, 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, SDK 1.4 adds a method requestFocusInWindow to the Component class. That method succeeds only if the component is contained in the focused window.

graphics/notes_icon.gif

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.

graphics/notes_icon.gif

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 this time, the focus is already lost. If you must trap the focus in a particular component, you need to 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

graphics/api_icon.gif
  • 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 isFocusOwnwer() 1.4

    returns true if this component currently owns the focus

java.awt.KeyboardFocusManager 1.4

graphics/api_icon.gif
  • 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

graphics/api_icon.gif
  • 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 are dialogs usually marked by the window manager.

java.awt.event.FocusEvent 1.1

graphics/api_icon.gif
  • Component getOppositeComponent() 1.4

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

java.awt.event.WindowEvent 1.4

graphics/api_icon.gif
  • Window getOppositeWindow() 1.4

    returns the window that lost the focus in the windowGainedFocus handler, the window that gained the 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

graphics/api_icon.gif
  • void windowGainedFocus(WindowEvent event)

    called when the event source window gained focus.

  • void windowLostFocus(WindowEvent event)

    called when the event source window lost focus.


       
    Top
     



    Core Java 2(c) Volume I - Fundamentals
    Building on Your AIX Investment: Moving Forward with IBM eServer pSeries in an On Demand World (MaxFacts Guidebook series)
    ISBN: 193164408X
    EAN: 2147483647
    Year: 2003
    Pages: 110
    Authors: Jim Hoskins

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