Changing Focus traversal Keys in Java V1.4

Baldwin shows you two different ways to change the focus traversal keys on an individual component at runtime.

Published:  August 5, 2003
By Richard G. Baldwin

Java Programming Notes # 1848


Preface

New features in SDK Version 1.4.0

The recently released JavaTM 2 SDK, Standard Edition Version 1.4 contains a large number of new features, including many changes and additions to the focus subsystem.  This lesson is part of a series of lessons designed to teach you how to use the new features of the focus subsystem in Java Version 1.4 and later.

The first lesson in the series was entitled Focus Traversal Policies in Java Version 1.4.  The previous lesson was entitled Focusability in Java Version 1.4.

Focus traversal keys

This lesson will concentrate on one important new aspect of focus traversal, focus traversal keys.  Subsequent lessons will deal with other aspects of focus traversal, as well as a variety of other features of the new focus subsystem.

A lot to learn

There is a lot to learn about the new focus subsystem.  It is anything but trivial.

In addition to new capabilities, Sun has also introduced a new set of focus terminology.  I have briefly discussed the following terms in the previous lessons in this series.

In addition, previous lessons have dealt with several aspects of the new focus subsystem, including:

This lesson will show you how to modify the focus traversal keys on individual components at runtime.

Viewing tip

You may find it useful to open another copy of this lesson in a separate browser window.  That will make it easier for you to scroll back and forth among the different listings and figures while you are reading about them.

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials.  You will find those lessons published at Gamelan.com.  However, as of the date of this writing, Gamelan doesn't maintain a consolidated index of my Java tutorial lessons, and sometimes they are difficult to locate there.  You will find a consolidated index at www.DickBaldwin.com.

Preview

In this lesson, I will teach you two different ways to change the focus traversal keys on an individual component at runtime.

Discussion and Sample Code

Focus traversal

Sun defines focus traversal as "the user's ability to change the "focus owner" without moving the cursor."

Focus traversal can normally be either forward to the next component, or backward to the previous component.

Traversal typically uses keys

Typically, focus traversal is accomplished using one or more keys on the keyboard.  For example, it is very common for the TAB key to be used to move the focus along its traversal path in the forward direction, and for the Shift-TAB key combination to be used to move the focus along its traversal path in the backward direction.

However, keyboard action isn't always required.  It is also possible for client code to initiate traversal through the execution of program instructions.

No longer tied to the TAB key

To the best of my knowledge, prior to the release of version 1.4, it was not possible for the programmer to specify the key or combination of keys used for focus traversal (if it was possible, I never figured out how to do it). 

With version 1.4, each component can individually define its own set of focus traversal keys for a given focus traversal operation.  For example, it is no longer required that the TAB and Shift-TAB keys be the keys used for focus traversal. 

Every component can have different traversal keys

Furthermore, with V1.4, every different component within a focus traversal cycle can use a different set of traversal keys, and the set of keys being used for any component can be modified at runtime.  (I will demonstrate this with the programs that I will discuss later in this lesson.)

Each component supports a separate set of keys for forward and backward traversal.  Separate sets of keys are also supported for traversal up one focus traversal cycle. Containers that are focus cycle roots also support a set of keys for traversal down one focus traversal cycle.

Traversal keys may be inherited

If a set of focus traversal keys is not explicitly defined for a component, that component recursively inherits a set from its parent.  If no set of focus traversal keys is explicitly defined somewhere along the way, the component ultimately inherits its set of focus traversal keys from a context-wide default set on the current KeyboardFocusManager.

AWTKeyStroke

As you will see later, a set of focus traversal keys for a particular component consists of a Set of references to objects of type AWTKeyStroke, where Set is a common interface used in the Java Collections Framework.  (If you are unfamiliar with the Java Collections Framework, you can read about it on my web site.)

Thus, the set of focus traversal keys for a particular component consists of one or more references to unique objects of type AWTKeyStroke.

What is an AWTKeyStroke?

According to Sun,

"An AWTKeyStroke represents a key action on the keyboard, or equivalent input device. AWTKeyStrokes can correspond to only a press or release of a particular key, just as KEY_PRESSED and KEY_RELEASED KeyEvents do; alternately, they can correspond to typing a specific Java character, just as KEY_TYPED KeyEvents do. In all cases, AWTKeyStrokes can specify modifiers (alt, shift, control, meta, or a combination thereof) which must be present during the action for an exact match."

KEY_TYPED is not allowed

Also according to Sun, although an AWTKeyStroke can correspond to typing a specific Java character,

"It is a runtime error to specify a KEY_TYPED event as mapping to a focus traversal operation, or to map the same event to multiple focus traversal operations for any particular Component or for a KeyboardFocusManager's defaults."

In other words, although an AWTKeyStroke can represent KEY_TYPED events, such a keystroke cannot be used as a focus traversal key.  Only KEY_PRESSED and KEY_RELEASED actions on the part of the user can be used for focus traversal keys. 

Client code specifies which to use

The client code can specify on which of the two specific actions, KEY_PRESSED or KEY_RELEASED, the focus traversal operation will occur.  This will also be demonstrated in the programs to be discussed later in this lesson.

KeyEvent is consumed

If the key that is pressed is a focus traversal key, the key events that would normally be generated by pressing and releasing the key (including the associated KEY_TYPED event), will be consumed.  Those key events will not be dispatched to any component (no registered key listeners will be notified).

Default focus traversal keys

According to Sun, the default focus traversal keys are implementation-dependent. Sun recommends that all implementations for a particular native platform use the same keys. For Windows and Unix, Sun's recommendations are:

Note that for the recommended default set, TextArea and JTextArea components are treated differently from other components (including text fields) with respect to the TAB key.

Disable a traversal key

Sun goes on to state,

"These recommendations are used in the Sun AWT implementations. ... To disable a traversal key, use an empty Set; Collections.EMPTY_SET is recommended."

Changing the focus traversal keys at runtime

The programs to be discussed later begin with the default focus traversal keys, change to a different set of focus traversal keys during runtime, and later restore the focus traversal keys to the defaults.

Enabling and disabling focus traversal

Components can enable and disable all of their focus traversal keys by invoking the setFocusTraversalKeysEnabled method of the Component class.

When focus traversal keys are disabled, the component receives all key events for those keys. However, as mentioned above, when focus traversal keys are enabled, the component never receives key events for traversal keys.  Keystrokes corresponding to focus traversal keys are mapped to focus traversal operations instead.

FocusTraversalPolicy

The AWT focus implementation determines which component to focus next based on the FocusTraversalPolicy of the focus owner's focus cycle root.  (The FocusTraversalPolicy was the subject of a previous lesson.)

Description of the programs

This tutorial presents two similar programs to illustrate many of the concepts mentioned above.  The programs are named FocusKeys01 and FocusKeys02.  Complete listings of both programs are presented for your examination in Listing 29 and Listing 30 near the end of the lesson.

Adding objects to a Set

The two programs differ in how they establish the set of AWTKeyStroke objects used to create a custom set of focus traversal keys for a JTextField object. 

An important issue has to do with restrictions imposed by the add method of a Set object.  In order to be eligible for adding to a set:

The more complex case

The more complex of the two cases is illustrated by the program named FocusKeys01.  In this program, the AWTKeyStroke class is extended into a new class that implements the Comparable interface.  Objects of the new class are used to define a custom set of focus traversal keys. 

This approach illustrates some interesting features of Java, and is discussed in detail in this tutorial lesson.

The less complex case

The less complex of the two cases is illustrated by the program named FocusKeys02.  This program defines a class that implements the Comparator interface.  An instance of that class can be used to compare two AWTKeyStroke objects.

The differences between the two programs are also explained in this lesson.

The graphical user interface

Both programs place three buttons and a text field in a frame, as shown in Figure 1.  Also, both programs exhibit the same behavior.  It is only the implementation of that behavior that differs between the two programs.

Figure 1.  Sample program user interface.

Traversal state

Initially, the default focus traversal keys shown in the earlier list from Sun apply to all four components. 

A new set of focus traversal keys

When the button labeled Change is pressed, a new set of focus traversal keys is created and applied to the text field only (the focus traversal keys for the three buttons don't change, illustrating that different focus traversal keys can be defined for each component).  From that point forward until the button labeled Restore is pressed, the new set of focus traversal keys must be used to traverse forward or backward from the text field.

Restore the default focus traversal keys

When the button labeled Restore is pressed, the default focus traversal keys are restored to the text field, and from that point forward until the Change button is pressed again, the default traversal keys must be used to traverse forward or backward from the text field.

What about key events?

A key listener is registered on the text field to demonstrate that focus traversal keys don't deliver key events to key-event handlers.

Miscellaneous information

Various kinds of information are displayed on the standard output device to illustrate the behavior of the program as it executes.

Will discuss sample program in fragments

As is my habit, I will discuss the program in fragments.  A complete listing of the program is provided in Listing 29 near the end of the lesson.

This program was tested using JDK 1.4.1 under WinXP, and requires Java version 1.4 or later to compile and run correctly.

Get a GUI object

Listing 1 shows the main method, which simply instantiates a new object of the GUI class.

class FocusKeys01{
public static void main(String[] args){
new GUI(); }// end main }//end class FocusKeys01 definition Listing 1

The GUI class

Listing 2 shows the beginning of the GUI class, along with the declaration of several instance variables, whose purpose will be become clear as the discussion progresses.

class GUI{
JFrame jFrame = new JFrame();
TreeSet forwardSet;
TreeSet backwardSet;
JTextField textField;
Set oldForwardSet;
Set oldBackwardSet;

Listing 2

The GUI constructor

Listing 3 shows the beginning of the constructor for the GUI class.  Listing 3 also shows a couple of routine housekeeping operations, which I won't discuss further.
 
  GUI(){//constructor
jFrame.setTitle(
"Copyright 2003, R.G.Baldwin");
jFrame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);

Listing 3

The GUI components

As you saw in Figure 1 earlier, the GUI object appears to consist of three buttons and a text field in a frame.  It actually consists of three JButton objects and a JTextField object, in a JPanel object, which in turn is placed in a JFrame object.

The JFrame object was created in Listing 2 above.  Listing 4 shows the creation of the panel, the three buttons, and the text field.  Code in a subsequent fragment will assemble these parts into the final GUI object.
 
    JPanel panel = new JPanel();
JButton changeBtn = new JButton("Change");
JButton restoreBtn = new JButton("Restore");
JButton dummyBtn = new JButton("Dummy");
textField = new JTextField("abcde");

Listing 4

A key listener

As I explained earlier, if a key is pressed while a component has the focus, and if the key that is pressed is a focus traversal key, the key event that would normally be generated by pressing the key will be consumed.  No key-event handler methods will be invoked on any key listener objects registered on that component.

The code in Listing 5 registers a key listener object on the text field using an anonymous inner class.  This key listener displays information about the key that generated the event.  It is used to demonstrate that key events resulting from focus traversal keys are consumed and not delivered to registered listener objects.
 
    textField.addKeyListener(
new KeyAdapter(){
public void keyPressed(KeyEvent e){
System.out.println(e.getKeyText(
e.getKeyCode()));}});

Listing 5

I will discuss the results of such a demonstration later in this tutorial lesson.

Save default focus traversal key sets

As I mentioned earlier, pressing the Restore button shown in Figure 1 causes the default set of focus traversal keys to be restored to the text field.  This is accomplished by saving the defaults as the program is starting up, and later using an action-event handler on the Restore button to apply the saved sets to the text field.

The code in Listing 6 saves the default traversal key sets for forward and backward traversal in two instance variables of type Set, which were declared in Listing 2.
 
    oldForwardSet = textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS);
oldBackwardSet = textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS);

Listing 6

An indexed property named focusTraversalKeys

If you are familiar with Java design patterns, you will recognize from Listing 6 that a JTextField object has an indexed property named focusTraversalKeys(This conclusion is based on the existence of the method named getFocusTraversalKeys, which takes an integer parameter.)

With a little experimentation, you can determine the values of four constants, two of which are used in Listing 6.

(These constants are defined in the KeyboardFocusManager class and are inherited into the DefaultKeyboardFocusManager class.)

Four collections of type Set

By default, the indexed property named focusTraversalKeys contains references to four different collections of type Set.  Each collection contains references to a set of AWTKeyStroke objects.  There is one set for forward traversal, one set for backward traversal, one set for up-cycle traversal, and one set for down-cycle traversal.  (I will be discussing up-cycle and down-cycle traversal in a future lesson.)

Get and save for later restoration

The code in Listing 6 gets and saves references to the sets corresponding to forward and backward traversal.  These sets will be used later to restore the default focus traversal keys for the text field when the Restore button is pressed.

What is the class type of the Set objects?

If we are going to create custom focus traversal keys for a component, we must encapsulate references to objects of type AWTKeyStroke in Set objects whose references will be stored in the focusTraversalKeys property of the text field.  Therefore, we need to know the name of a class from which we can instantiate those Set objects.  So far, I haven't found an explicit specification of the name of that class in the Sun documentation.  Some experimentation is required in order to identify that class.

Objects of type Set are required

The getFocusTraversalKeys method of the JFrame class is inherited from the Container class.  An examination of the documentation for that method tells us that the method returns a reference to an object of type Set.

Similarly, examination of the documentation for the Container class reveals the existence of the following method as well:

public void setFocusTraversalKeys(
int id, Set keystrokes)

However, all this really tells us is that the type of the property named focusTraversalKeys is the interface type Set.  In theory, we could use a reference to an object of any class that implements the interface named Set as a legitimate value for the property.

Practical considerations

However, there may be practical considerations indicating that the use of some sets would be better than the use of other sets.  I will consider Sun to be the authority in making a choice among different types of sets.

What type does Sun use?

The code in Listing 7 gets and displays the type of object that Sun uses to encapsulate the default focus traversal keys.  It should be safe for us to use the same type.
 
    System.out.println("Type of set");
System.out.println(oldForwardSet.getClass());

Listing 7

UnmodifiableSet

Listing 7 produces the screen output shown in Figure 2, indicating that Sun uses UnmodifiableSet, which is available via a method of the Collections class.
 
Type of set
class java.util.Collections$UnmodifiableSet

Figure 2

What did we learn?

This is a very interesting result.  As it turns out, we still don't know for sure the type of object used by Sun to encapsulate the default focus traversal keys.  However, an examination of the documentation for the Collections class reveals about six similar methods having names beginning with the word unmodifiable.  The method named unmodifiableSet is one of those methods.  

An unmodifiable view of a Set object

The documentation for the unmodifiableSet method of the Collections class provides the following information:

"Returns an unmodifiable view of the specified set. This method allows modules to provide users with "read-only" access to internal sets. Query operations on the returned set "read through" to the specified set, and attempts to modify the returned set, whether direct or via its iterator, result in an UnsupportedOperationException."

A conclusion

From this information, I concluded that it doesn't really matter what class is used to instantiate the Set object used to encapsulate the focus traversal keys so long as an unmodifiable view of the Set object is passed to the setFocusTraversalKeys method. 

What is an unmodifiable view?

The use of an unmodifiable view means that the programmer can cause the property to refer to a different set, but cannot modify the contents of the set currently referred to by the property.

"As it turns out, it is easy to demonstrate that the use of an unmodifiable view is not a technical requirement.  A reference to any Set object can be used.  However, it is probably a very good idea to follow Sun's example and make the set unmodifiable."

Later on in the program, when I create a custom set of focus traversal keys, I will use an unmodifiable view of a TreeSet object.

Register an ActionListener object on the buttons

The code in Listing 8 creates an action listener object and registers it on the two left-most buttons shown in Figure 1.  (The right-most button is a dummy button used solely to illustrate focus traversal.  It doesn't have any listeners registered on it.)
 
    MyActionListener listener =
new MyActionListener();
changeBtn.addActionListener(listener);
restoreBtn.addActionListener(listener);

Listing 8

I will discuss the behavior of this action listener object in detail later in this lesson.

The remainder of the constructor

The remainder of the constructor is shown in Listing 9.  The code in Listing 9 does some housekeeping chores that are described in the comments, so I won't discuss them further.
 
    //Add a panel to the frame.  Then add the
// buttons and the text field to the panel.
jFrame.getContentPane().add(
panel,BorderLayout.CENTER);

panel.add(changeBtn);
panel.add(restoreBtn);
panel.add(textField);
panel.add(dummyBtn);

//Set the size of the frame and make it
// visible.
jFrame.setSize(400,100);
jFrame.setVisible(true);

}//end constructor

Listing 9

Adding an object to a Set

Earlier I explained that in order for an object to be added to a set:

This is because a Set object doesn't allow duplicates, and it must be possible for the add method of the Set object to be able to compare objects and to reject duplicates.

The implementing classes of the Set interface

The only known implementing classes of the Set interface in version 1.4.1 are:

I have no desire to define a new class that implements the Set interface, and would prefer to use an existing class.  Without getting into a lot of detail as to my reasons, I will state that of these four classes, only the TreeSet class is really suitable for use in this program.

TreeSet is a SortedSet

The TreeSet class implements the SortedSet interface, which Sun describes as follows:

"A set that further guarantees that its iterator will traverse the set in ascending element order, sorted according to the natural ordering of its elements (see Comparable), or by a Comparator provided at sorted set creation time."

Implementing Comparable

The AWTKeyStroke class does not implement the Comparable interface.

The program under immediate discussion is named FocusKeys01.  In this program, I extend the AWTKeyStroke class into a new class named MyAWTKeyStroke, which implements the Comparable interface. 

Because objects of this new class are AWTKeyStroke objects, they are suitable for use in defining a custom set of focus traversal keys. 

Because objects of this new class implement the Comparable interface, they are suitable for adding to a Set object.

The class named MyAWTKeyStroke

Listing 10 shows the beginning of the new class named MyAWTKeyStroke, which extends AWTKeyStroke and implements Comparable.
 
class MyAWTKeyStroke extends AWTKeyStroke
implements Comparable{

Listing 10

Now we come to a very interesting issue involving the AWTKeyStroke class.

The getAWTKeyStroke method

To begin with, the documentation of the AWTKeyStroke class contains the following statement:

"AWTKeyStrokes are immutable, and are intended to be unique. Client code should never create an AWTKeyStroke on its own, but should instead use a variant of getAWTKeyStroke. Client use of these factory methods allows the AWTKeyStroke implementation to cache and share instances efficiently."

The appropriate version of getAWTKeyStroke

The AWTKeyStroke class provides several overloaded public static methods named getAWTKeyStroke.  The version that is appropriate for use in creating focus traversal keys is described in Figure 3.
 
AWTKeyStroke getAWTKeyStroke(
int keyCode,
int modifiers,
boolean onKeyRelease)

The "virtual key" constants defined in
java.awt.event.KeyEvent can be used to
specify the key code. For example:
VK_ENTER
VK_TAB
VK_SPACE
The modifiers consist of any combination
of the following constants of the
java.awt.event.InputEvent class:
SHIFT_DOWN_MASK
CTRL_DOWN_MASK
META_DOWN_MASK
ALT_DOWN_MASK
ALT_GRAPH_DOWN_MASK
BUTTON1_DOWN_MASK
BUTTON2_DOWN_MASK
BUTTON3_DOWN_MASK
The third parameter, onKeyRelease, should
be true if the AWTKeyStroke should
represent a key release; false otherwise.

Figure 3

An important question about factory methods

Since Sun insists that this factory method be used to get AWTKeyStroke objects, how does one go about creating a similar factory method for a subclass of AWTKeyStroke?

The answer comes in the form of a protected static method of the AWTKeyStroke class named registerSubclass.  A description of this method is given in Figure 4.

void registerSubclass(Class subclass)

Registers a new class which the factory
methods in AWTKeyStroke will use when
generating new instances of AWTKeyStrokes.
After invoking this method, the factory
methods will return instances of the
specified Class. The specified Class must
be either AWTKeyStroke or derived from
AWTKeyStroke, and it must have a no-arg
constructor. The constructor can be of any
accessibility, including private. This
operation flushes the current AWTKeyStroke
cache.

Parameters:
subclass - the new Class of which the
factory methods should create instances

Figure 4

Registration is the key

In other words, if you extend the AWTKeyStroke class into a new class, you should invoke the registerSubclass method of the AWTKeyStroke class at least once in your program to cause the getAWTKeyStroke method of the AWTKeyStroke class to return an instance of your new class instead of returning an instance of the AWTKeyStroke class.

(We will learn later that even in the default case, the getAWTKeyStroke method doesn't actually return a reference to an object of the AWTKeyStroke class. Rather, it returns a reference to an instance of a subclass of the AWTKeyStroke class.)

The registration method

And that brings us to Listing 11, which defines a convenience method of the MyAWTKeyStroke subclass designed to register the subclass on the factory methods of the AWTKeyStroke class.

  static void registerIt(){
try{
registerSubclass(Class.forName(
"MyAWTKeyStroke"));
}catch(Exception ex){System.out.println(ex);}
}//end registerIt

Listing 11

The registerIt method of the MyAWTKeyStroke class registers the subclass with the factory methods of the AWTKeyStroke class, causing the factory methods of that class to return a reference to an instance of the subclass.

The code in Listing 11 invokes the static registration method of the AWTKeyStroke class, passing a reference to a Class object representing the MyAWTKeyStroke class as a parameter.

Not a Comparable interface issue

Note that this requirement for registration results from extending the AWTKeyStroke class, and has nothing to do with implementing the Comparable interface, which is the reason that I extended the class in the first place.

The Comparable interface

Here is a little of what Sun has to say about the Comparable interface:

"This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class's natural ordering, and the class's compareTo method is referred to as its natural comparison method."

The compareTo method

Any non-abstract class that implements the Comparable interface must provide a concrete definition of the method named

compareTo(Object o)

Here is a little of what Sun has to say about the compareTo method:

"Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object."

My compareTo method

Listing 12 defines the overridden compareTo method for the MyAWTKeyStroke class. The comparison is based on the String representation of the two objects, using the version of the compareTo method defined in the String class to perform the actual comparison.
 
  public int compareTo(Object o){
String str1 = this.toString();
String str2 = o.toString();
return str1.compareTo(str2);
}//end compareTo
}//end MyAWTKeyStroke

Listing 12

That completes the discussion of the class named MyAWTKeyStroke, which extends AWTKeyStroke, and implements Comparable.

The action listener class

Listing 13 shows the beginning of the inner class named MyActionListener, which is used to instantiate and register an action listener object on the two leftmost buttons shown in Figure 1. This class is an inner class of the class named GUI.

Listing 13 also shows the beginning of the actionPerformed method, which is required of any class that implements the ActionListener interface. This is the method that is invoked whenever the user clicks on the Change button or the Restore button in Figure 1.
 
  class MyActionListener
implements ActionListener{

public void actionPerformed(ActionEvent e){

Listing 13

Registering the subclass on the factory methods

The actionPerformed method begins by invoking the registerIt method discussed above to cause an object of the MyAWTKeyStroke class to be returned when the getAWTKeyStroke method is invoked later. This code is shown in Listing 14 (The convenience class named registerIt is defined in Listing 11.)
 
      MyAWTKeyStroke.registerIt();

Listing 14

Display the current focus traversal keys for the text field

Next, the code in Listing 15 uses methods and constants discussed earlier to get and display the current focus traversal keys for the text field.
 
      System.out.println(
"Current focus-traversal keys");
System.out.println(textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS));
System.out.println(textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS));

Listing 15

The default focus traversal keys

The first time either of the left-most buttons in Figure 1 is pressed, the output shown in Figure 5 is produced by the code in Listing 15.
 
Current focus-traversal keys
[keyCode CtrlTab-P, keyCode Tab-P]
[keyCode Ctrl+ShiftTab-P, keyCode ShiftTab-P]

Figure 5

What you are seeing in Figure 5 is the default focus traversal keys for the text field, before any changes have been made to those keys. If you compare this with the defaults for a Windows operating system discussed earlier, you will see that there is a match.

When the Change button is pressed ...

At this point, the actionPerformed method needs to determine which button was pressed, (the Change button or the Restore button), and to take the action appropriate for that button.

Listing 16 shows the beginning of the action taken for the case where the Change button was pressed.

      if(e.getActionCommand().equals("Change")){
forwardSet = new TreeSet();
backwardSet = new TreeSet();

Listing 16

Create two new Set objects

When the Change button is pressed, the code in Listing 16 instantiates two new TreeSet objects and stores their references in a pair of instance variables that were declared in Listing 2.

As you can probably guess from the variable names, one of these Set objects will be used to store keystrokes for forward traversal, and the other will be used to store keystrokes for backward traversal.

Populate the Set objects

The next task is to populate each of the new Set objects with the keystrokes that will be used for traversal in the forward and backward directions. It is important to note that any number of different keystrokes can be encapsulated in the set. In this program, I elected to encapsulate two different keystrokes for each direction of traversal.

Add two AWTKeyStroke objects to one set

The code in Listing 17 uses the add method of the TreeSet class along with the getAWTKeyStroke factory method discussed earlier, to encapsulate two different keystrokes in the set for forward traversal. The objects encapsulated in the set are instances of the class MyAWTKeyStroke, (as a result of the registration discussed earlier).
 
        //Create and add two keystrokes for
// forward traversal - lower or upper
// case F on key pressed. Uses factory
// method to return an instance of the
// registered subclass.
forwardSet.add(AWTKeyStroke.
getAWTKeyStroke(
KeyEvent.VK_F,0,false));

forwardSet.add(AWTKeyStroke.
getAWTKeyStroke(
KeyEvent.VK_F,
InputEvent.SHIFT_DOWN_MASK,
false));

Listing 17

There are two statements in Listing 17. The first statement encapsulates a lower-case f in the set, while the second statement encapsulates an upper-case F in the set. In both cases, the traversal action will occur when the key is pressed and not when it is released.

Add two AWTKeyStroke objects to the other set

In a similar manner, the code in Listing 18 encapsulates two keystrokes in the set that will be used for backward traversal out of the text field component.
 
        //Create and add two keystrokes for
// backward traversal - lower case b on
// key released and upper case B on key
// pressed.
backwardSet.add(AWTKeyStroke.
getAWTKeyStroke(
KeyEvent.VK_B,0,true));

backwardSet.add(AWTKeyStroke.
getAWTKeyStroke(
KeyEvent.VK_B,
InputEvent.SHIFT_DOWN_MASK,
false));

Listing 18

In this case, the keystrokes are the lower and upper-case versions of the character B. However, in this case, the traversal action is designed to occur on key release instead of key press for the lower-case b. As before, the traversal action is designed to occur on key press for the upper-case B.

Display keystroke type

Just to confirm that things are going as planned, the code in Listing 19 gets and displays the type of the first keystroke stored in the forward set.
 
        System.out.println("Keystroke type");
System.out.println(forwardSet.first().
getClass());

Listing 19

The output produced by the code in Listing 19 is shown in Figure 6.
 
Keystroke type
class MyAWTKeyStroke Figure 6

Happily, the result is what we expected, confirming that the process of registering the subclass with the factory method worked properly.

Get an unmodifiable view

At this point, it would be technically possible to use the populated objects of the TreeSet class to modify the focusTraversalKeys property of the text field. However, it is better programming practice to get an unmodifiable view object for each TreeSet object and to use those view objects to set the new property values.

The code in Listing 20 uses the unmodifiableSortedSet method of the Collections class to accomplish this.
 
        Set unmodifiableForwardSet = Collections.
unmodifiableSortedSet(
forwardSet);

Set unmodifiableBackwardSet =Collections.
unmodifiableSortedSet(
backwardSet);

Listing 20

Display type of unmodifiable set

Just to make certain that everything is still going according to plan, the code in Listing 21 gets and displays the actual type of one of the view objects.
 
        //Get and display type of unmodifiable
// set
System.out.println("Type of set");
System.out.println(
unmodifiableForwardSet.
getClass());

Listing 21

The output produced by Listing 21 is shown in Figure 7.
 
Type of set
class java.util.Collections$UnmodifiableSortedSet Figure 7

Happily again, the result matches our expectation, so things must be going well.

Modify the focusTraversalKeys property

Finally, Listing 22 shows the code that actually modifies the focusTraversalKeys property values for the text field.
 
        textField.setFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS,
unmodifiableForwardSet);

textField.setFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS,
unmodifiableBackwardSet);


Listing 22

There are two statements in Listing 22. The first statement sets the value of the indexed focusTraversalKeys property corresponding to forward traversal.

The second statement sets the value of the indexed focusTraversalKeys property corresponding to backwards traversal.

In each case, the property value is set to refer to one of the unmodifiable set objects created in Listing 20.

Display new focus traversal keys

Although we haven't seen it yet, there is some code at the end of this action event handler method that displays the new focus traversal keys. For the case where the event handler was invoked because the Change button was pressed, the output produced by that code is shown in Figure 8.
 
New focus-traversal keys
[keyCode ShiftF-P, keyCode F-P]
[keyCode ShiftB-P, keyCode B-R]

Figure 8

This output shows the new focus traversal keys for the forward direction to be lower and upper-case F on key pressed.

The output shows the new focus traversal keys for the backward direction to upper-case B on key pressed, and lower-case b on key released.

These results are exactly what we expected them to be.

That completes the discussion of the result of pressing the Change button.

Pressing the Restore button

Listing 23 shows the action that results from pressing the Restore button.
 
      }else{
//If the Restore button was clicked,
// restore the original focus properties
// on the text field
textField.setFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS,
oldForwardSet);
textField.setFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS,
oldBackwardSet);
}//end else

Listing 23

This action is very straightforward. In this case, the two values of the indexed focusTraversalKeys property are simply restored to the default values that were saved earlier.

Display the focus traversal keys

For the case where the Restore button is pressed after first pressing the Change button, the forward and backward focus traversal keys on entry to the action event handler method are shown in Figure 9.
 
Current focus-traversal keys
[keyCode ShiftF-P, keyCode F-P]
[keyCode ShiftB-P, keyCode B-R]

Figure 9

For the same case, the forward and backward focus traversal keys on exit from the action event handler method are shown in Figure 10.

New focus-traversal keys
[keyCode Tab-P, keyCode CtrlTab-P]
[keyCode ShiftTab-P, keyCode Ctrl+ShiftTab-P]

Figure 10

Just for the record, Listing 24 shows the code that displays the focus traversal keys on exit from the action event handler method. There is nothing new in this code, so I won't discuss it further.

      System.out.println(
"New focus-traversal keys");
System.out.println(textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS));

System.out.println(textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS));
}//end actionPerformed
}//end class MyActionListener
//-------------------------------------------//
}//end class GUI

Listing 24

The program named FocusKeys02

Before leaving the topic of focus traversal keys, I want to provide a brief discussion of the program named FocusKeys02. This program uses the AWTKeyStroke class directly and implements the Comparator interface instead of extending the AWTKeyStroke class and implementing the Comparable interface.

A complete listing of FocusKeys02 can be viewed in Listing 30 near the end of the lesson. I will show and discuss a few code fragments from this program that differ from the similar parts of the program named FocusKeys01.

Two TreeSet objects

The code fragment in Listing 25 comes from the inner class used to create an action listener object that is registered on the two left-most buttons in Figure 1.

The code shows the creation of two new TreeSet objects for the case where the user has pressed the Change button.
 
  class MyActionListener
implements ActionListener{

public void actionPerformed(ActionEvent e){

// ... code deleted for brevity

if(e.getActionCommand().equals("Change")){
forwardSet = new TreeSet(
new AWTKeyStrokeComparator());
backwardSet = new TreeSet(
new AWTKeyStrokeComparator());

Listing 25

Using a Comparator

The important thing to note in Listing 25 is the use of the TreeSet constructor that requires a reference to an object instantiated from a class that implements the Comparator interface. (I will show you the definition of that class later.) The Comparator object is used by the add method of the TreeSet object to reject duplicates and to sort the contents of the set.

The getAWTKeyStroke method again

As is the case for FocusKeys01, this program uses the getAWTKeyStroke method to create the keystrokes used to populate the two sets for forward and backward traversal.

Listing 26 shows the code that populates the set for forward traversal. As before, the new forward traversal keys are lower-case and upper-case F.
 
        forwardSet.add(AWTKeyStroke.
getAWTKeyStroke(
KeyEvent.VK_F,0,false));

forwardSet.add(AWTKeyStroke.
getAWTKeyStroke(
KeyEvent.VK_F,
InputEvent.SHIFT_DOWN_MASK,
false));

Listing 26

No registration required

Unlike the case in the program named FocusKeys01, this program does not extend the AWTKeyStroke class into a new class that implements the Comparable interface. Thus, there is no requirement to register a subclass with the factory methods of the AWTKeyStroke class.

Get and display the keystroke object type

This causes us to wonder about the actual type of object that is returned when the getAWTKeyStroke method is invoked in Listing 26. The answer to that question is provided by the code in Listing 27.
 
        System.out.println("Keystroke type");
System.out.println(forwardSet.first().
getClass());

Listing 27

The code in Listing 27 gets and displays the type of the object stored in the first element of the forward set. The output produced by Listing 27 is shown in Figure 11.
 
Keystroke type
class javax.swing.KeyStroke Figure 11

Interestingly, even in this case, the type of the object that represents the keystroke is not AWTKeyStroke. Rather, it is type KeyStroke, which is the only known subclass of AWTKeyStroke in version 1.4. (For the record, the KeyStroke class does not implement Comparable.)

What does Sun have to say?

The documentation describes the KeyStroke class very similarly to the description of the AWTKeyStroke class, and there is no obvious indication as to why it is the better choice between the two. However, the constructors for the AWTKeyStroke class are protected. Therefore, an object of type AWTKeyStroke can only be created by creating an instance of a subclass of AWTKeyStroke.

Set focusTraversalKeys property values

As in the case of FocusKeys01, the code goes on to get unmodifiable views of the populated sets, and uses those view objects to modify the values stored in the indexed property named focusTraversalKeys.

A Comparator object

That brings us to the class of my own design named AWTKeyStrokeComparator. This class implements the Comparator interface. Objects of this class were passed to the constructors for the TreeSet objects in Listing 25. The Comparator objects are used by the add method of the TreeSet objects to reject duplicates and to sort the contents of the set.

The compare method

A class that implements the Comparator interface must define a method named compare. As the name implies, this is the method belonging to the object that is actually used to compare two objects.

Here is part of what Sun has to say about the Comparator interface:

"A comparison function, which imposes a total ordering on some collection of objects. Comparators can be passed to a sort method (such as Collections.sort) to allow precise control over the sort order. Comparators can also be used to control the order of certain data structures (such as TreeSet or TreeMap)."

The AWTKeyStrokeComparator class

The entire class named AWTKeyStrokeComparator is shown in Listing 28.
 
class AWTKeyStrokeComparator
implements Comparator{

public int compare(Object o1,Object o2){
return o1.toString().
compareTo(o2.toString());
}//end compare

}//end AWTKeyStrokeComparator

Listing 28

As in the program named FocusKeys01, I made the comparison between the two objects based on the String representation of the objects. Once again, I took advantage of the compareTo method of the String class to accomplish the comparison.

The equals method

Interestingly, the Comparator interface also declares an equals method with a signature identical to the equals method that every class inherits from the Object class. Therefore, the requirement to provide a definition of the equals method of the Comparator interface is technically satisfied by the inherited equals method.

The Sun documentation contains the following statement:

"Note that it is always safe not to override Object.equals(Object). However, overriding this method may, in some cases, improve performance by allowing programs to determine that two distinct Comparators impose the same order."

In this case, I elected not to override the equals method.

Run the Program

If you haven't already done so, I encourage you to copy the code from Listing 29 or Listing 30 into your text editor, compile it, and execute it.  Experiment with it, pressing buttons and keys, and observing the results of your actions.

Remember, however, that you must be running Java version 1.4 or later to compile and execute this program.

Operational behavior

When the program first starts running, successive presses of the TAB key should cause the focus to traverse from left to right across all four components, including the text field. Similarly, pressing Shift-TAB should cause the focus to traverse across all four components in reverse order.

The key event handler

When the program is in this state and the text field has the focus, pressing just about any key on the keyboard (other than TAB), will deliver a key event to the key event handler.

The key event handler will display an identification of the key that was pressed. As mentioned earlier, the key event associated with the TAB key is consumed and is not delivered to the event handler because the TAB key is a focus traversal key.

Changing the focus traversal keys

Pressing the Change button causes the focus traversal keys for the text field to change (the focus traversal keys for the buttons remain the same as before).

As described earlier, the new forward focus traversal keys for the text field are lower-case and upper-case F. The new backward focus traversal keys for the text field are lower-case and upper-case B.

In addition, the traversal action for lower-case b occurs on key released instead of key pressed.

The key event handler

When the program is in this state and the text field has the focus, pressing just about any key on the keyboard (other than f, F, b, or B), will deliver a key event to the key event handler. This includes the TAB key, because it is no longer a focus traversal key. (Note, however, that the text field shows no indication that the TAB key has been pressed.)

As before, the key event handler displays the identification of the key.

Also as before, when any of the focus traversal keys (f, F, b, or B) are pressed, the key event associated with the key press is consumed and the event is not delivered to the key event handler.

Summary

In this lesson, I have taught you two different ways to change the focus traversal keys on an individual component at runtime.

What's Next?

Future lessons will discuss new focus features of version 1.4 including the following:

Complete Program Listing

Complete listings of the programs discussed in this lesson are shown in Listings 29 and 30 below.
 
/* File FocusKeys01.java
Copyright 2003, R.G.Baldwin

Illustrates the ability to change the focus
traversal keys. Also illustrates that a
component does not multicast a key event when it
has the focus and a focus traversal key is
pressed.

This version implements the comparable interface
to allow new focus traversal keys to be added to
a set.

Tested using JDK 1.4.1 under WinXP

This program requires v1.4.0 or later to compile
and run correctly.
************************************************/

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

class FocusKeys01{
public static void main(String[] args){
new GUI();
}// end main
}//end class FocusKeys01 definition
//=============================================//

class GUI{
JFrame jFrame = new JFrame();
TreeSet forwardSet;
TreeSet backwardSet;
JTextField textField;
Set oldForwardSet;
Set oldBackwardSet;

GUI(){//constructor
jFrame.setTitle(
"Copyright 2003, R.G.Baldwin");
jFrame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);

JPanel panel = new JPanel();
JButton changeBtn = new JButton("Change");
JButton restoreBtn = new JButton("Restore");
JButton dummyBtn = new JButton("Dummy");
textField = new JTextField("abcde");

//Register a key listener on the text field
// to illustrate the relationship between
// focus traversal keys and key events.
textField.addKeyListener(
new KeyAdapter(){
public void keyPressed(KeyEvent e){
System.out.println(e.getKeyText(
e.getKeyCode()));}});

//Save the default text field traversal keys
// for later restoration.
oldForwardSet = textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS);
oldBackwardSet = textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS);

//Get and display the type of the set object
// containing default focus traversal keys.
System.out.println("Type of set");
System.out.println(oldForwardSet.getClass());

//Create and register an action listener on
// two of the buttons. The third button is
// an inactive button used solely to
// illustrate focus traversal.
MyActionListener listener =
new MyActionListener();
changeBtn.addActionListener(listener);
restoreBtn.addActionListener(listener);

//Add a panel to the frame. Then add the
// buttons and the text field to the panel.
jFrame.getContentPane().add(
panel,BorderLayout.CENTER);

panel.add(changeBtn);
panel.add(restoreBtn);
panel.add(textField);
panel.add(dummyBtn);

//Set the size of the frame and make it
// visible.
jFrame.setSize(400,100);
jFrame.setVisible(true);

}//end constructor
//-------------------------------------------//

//This is an inner class, used to create an
// action listener for two of the buttons.
class MyActionListener
implements ActionListener{

public void actionPerformed(ActionEvent e){

//Register the named class with the factory
// methods of the AWTKeyStroke class.
MyAWTKeyStroke.registerIt();
//Display the existing focus traversal
// keystrokes on the text field.
System.out.println(
"Current focus-traversal keys");
System.out.println(textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS));
System.out.println(textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS));

//Identify source button and take
// appropriate action.
if(e.getActionCommand().equals("Change")){
//If the Change button was clicked,
// create new set objects to contain the
// new focus traversal keys.
forwardSet = new TreeSet();
backwardSet = new TreeSet();

//Create and add two keystrokes for
// forward traversal - lower or upper
// case F on key pressed. Uses factory
// method to return an instance of the
// registered subclass.
forwardSet.add(AWTKeyStroke.
getAWTKeyStroke(
KeyEvent.VK_F,0,false));
forwardSet.add(AWTKeyStroke.
getAWTKeyStroke(
KeyEvent.VK_F,
InputEvent.SHIFT_DOWN_MASK,
false));

//Create and add two keystrokes for
// backward traversal - lower case b on
// key released and upper case B on key
// pressed.
backwardSet.add(AWTKeyStroke.
getAWTKeyStroke(
KeyEvent.VK_B,0,true));
backwardSet.add(AWTKeyStroke.
getAWTKeyStroke(
KeyEvent.VK_B,
InputEvent.SHIFT_DOWN_MASK,
false));

//Get and display actual keystroke type
System.out.println("Keystroke type");
System.out.println(forwardSet.first().
getClass());

//Get unmodifiable views of the two sets.
// Note that this is not strictly
// required but is highly desirable.
Set unmodifiableForwardSet = Collections.
unmodifiableSortedSet(
forwardSet);
Set unmodifiableBackwardSet =Collections.
unmodifiableSortedSet(
backwardSet);

//Get and display type of unmodifiable
// set
System.out.println("Type of set");
System.out.println(
unmodifiableForwardSet.
getClass());

//Set the focus properties on the text
// field to the new focus-traversal
// keystrokes.
textField.setFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS,
unmodifiableForwardSet);
textField.setFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS,
unmodifiableBackwardSet);

}else{
//If the Restore button was clicked,
// restore the original focus properties
// on the text field
textField.setFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS,
oldForwardSet);
textField.setFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS,
oldBackwardSet);
}//end else

//Display the new focus traversal
// keys.
System.out.println(
"New focus-traversal keys");
System.out.println(textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS));
System.out.println(textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS));
}//end actionPerformed
}//end class MyActionListener
//-------------------------------------------//
}//end class GUI
//=============================================//

//An object of this class is suitable for adding
// to a set because it implements the Comparable
// interface.
class MyAWTKeyStroke extends AWTKeyStroke
implements Comparable{

//This method registers this subclass with the
// factory methods of the AWTKeyStroke class,
// causing the factory methods to return a
// reference to an instance of this subclass.
static void registerIt(){
//Invoke the static registration method
// passing a Class object representing this
// class as a parameter.
try{
registerSubclass(Class.forName(
"MyAWTKeyStroke"));
}catch(Exception ex){System.out.println(ex);}
}//end registerIt

//Define the compareTo method that is declared
// in the Comparable interface. Base the
// comparison on the String representation of
// the two objects.
public int compareTo(Object o){
String str1 = this.toString();
String str2 = o.toString();
return str1.compareTo(str2);
}//end compareTo
}//end MyAWTKeyStroke

Listing 29


 

/* File FocusKeys02.java
Copyright 2003, R.G.Baldwin

Illustrates the ability to change the
focus traversal keys. Also illustrates
that a component does not multicast a
key event when it has the focus and a
focus traversal key is pressed.

This version implements the Comparator
interface to allow new focus traversal
keys to be added to a set. See
Focus01 for a version that uses the
Comparable interface.

Tested using JDK 1.4.1 under WinXP.

This program requires v1.4.0 or later
to compile and run correctly.
**************************************/

import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

class FocusKeys02{
public static void main(
String[] args){
new GUI();
}// end main
}//end class FocusKeys02 definition
//===================================//

class GUI{
JFrame jFrame = new JFrame();
TreeSet forwardSet;
TreeSet backwardSet;
JTextField textField;
Set oldForwardSet;
Set oldBackwardSet;

GUI(){//constructor
jFrame.setDefaultCloseOperation(
JFrame.EXIT_ON_CLOSE);

JPanel panel = new JPanel();
JButton changeBtn =
new JButton("Change");
JButton restoreBtn =
new JButton("Restore");
JButton dummyBtn =
new JButton("Dummy");
textField =
new JTextField("abcde");

//Register a key listener on the
// text field to illustrate the
// relationship between focus
// traversal keys and key events.
textField.addKeyListener(
new KeyAdapter(){
public void keyPressed(
KeyEvent e){
System.out.println(
e.getKeyText(
e.getKeyCode()));}});

//Save the default text field
// traversal keys for later
// restoration.
oldForwardSet = textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS);
oldBackwardSet = textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS);

//Get and display the type of the
// set object containing default
// focus traversal keys.
System.out.println("Type of set");
System.out.println(
oldForwardSet.getClass());

//Create and register an action
// listener on two of the buttons.
// The third button is an inactive
// button used solely to illustrate
// focus traversal.
MyActionListener listener =
new MyActionListener();
changeBtn.addActionListener(
listener);
restoreBtn.addActionListener(
listener);

//Add a panel to the frame. Then
// add the buttons and the text
// field to the panel.
jFrame.getContentPane().add(
panel,BorderLayout.CENTER);

panel.add(changeBtn);
panel.add(restoreBtn);
panel.add(textField);
panel.add(dummyBtn);

//Set the size of the frame and
// make it visible.
jFrame.setSize(400,100);
jFrame.setVisible(true);

}//end constructor
//---------------------------------//

//This is an inner class, used to
// create an action listener for two
// of the buttons.
class MyActionListener
implements ActionListener{

public void actionPerformed(
ActionEvent e){

//Display the existing focus
// traversal keystrokes on the
// text field.
System.out.println(
"Current focus-traversal keys");
System.out.println(textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS));
System.out.println(textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS));

//Identify source button and take
// appropriate action.
if(e.getActionCommand().equals(
"Change")){
//If the Change button was
// clicked, create new set
// objects to contain the new
// focus traversal keys. Use
// the constructor that takes
// a reference to a Comparator.
forwardSet = new TreeSet(
new AWTKeyStrokeComparator());
backwardSet = new TreeSet(
new AWTKeyStrokeComparator());

//Create and add two keystrokes
// for forward traversal -
// lower or upper case F on key
// pressed. Uses factory
// method to return an instance
// of the registered subclass.
forwardSet.add(AWTKeyStroke.
getAWTKeyStroke(
KeyEvent.VK_F,0,false));
forwardSet.add(AWTKeyStroke.
getAWTKeyStroke(
KeyEvent.VK_F,
InputEvent.SHIFT_DOWN_MASK,
false));
//Create and add two keystrokes
// for backward traversal -
// lower case b on key released
// and upper case B on key
// pressed.
backwardSet.add(AWTKeyStroke.
getAWTKeyStroke(
KeyEvent.VK_B,0,true));
backwardSet.add(AWTKeyStroke.
getAWTKeyStroke(
KeyEvent.VK_B,
InputEvent.SHIFT_DOWN_MASK,
false));

//Get and display actual
// keystroke type
System.out.println(
"Keystroke type");
System.out.println(
forwardSet.first().
getClass());

//Get unmodifiable views of the
// two sets. Note that this
// is not strictly required.
Set unmodifiableForwardSet =
Collections.
unmodifiableSortedSet(
forwardSet);
Set unmodifiableBackwardSet =
Collections.
unmodifiableSortedSet(
backwardSet);

//Get and display type of
// unmodifiable set
System.out.println(
"Type of set");
System.out.println(
unmodifiableForwardSet.
getClass());

//Set the focus properties on
// the text field to the new
// focus-traversal keystrokes.
textField.
setFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS,
unmodifiableForwardSet);
textField.
setFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS,
unmodifiableBackwardSet);

}else{
//If the Restore button was
// clicked, restore the
// original focus properties on
// the text field
textField.
setFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS,
oldForwardSet);
textField.
setFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS,
oldBackwardSet);
}//end else

//Display the new focus traversal
// keys.
System.out.println(
"New focus-traversal keys");
System.out.println(textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
FORWARD_TRAVERSAL_KEYS));
System.out.println(textField.
getFocusTraversalKeys(
DefaultKeyboardFocusManager.
BACKWARD_TRAVERSAL_KEYS));
}//end actionPerformed
}//end class MyActionListener
//---------------------------------//
}//end class GUI
//===================================//

//This class makes it possible to add
// objects of type AWTKeyStroke to a
// set object, by providing a compare()
// method that can be used to compare
// two objects of type AWTKeyStroke.

//An object of this class must be
// passed to the constructor for the
// set object. The compare method
// defined here is based on the String
// representation of the objects.
class AWTKeyStrokeComparator
implements Comparator{

public int compare(Object o1,
Object o2){
return o1.toString().
compareTo(o2.toString());
}//end compare

//The Sun docs show an equals()
// method in the description of the
// Comparator interface. However:
//From the Sun docs: "Note that it is
// always safe not to override
// Object.equals(Object). However,
// overriding this method may, in
// some cases, improve performance by
// allowing programs to determine
// that two distinct Comparators
// impose the same order."

}//end AWTKeyStrokeComparator

Listing 30


Copyright 2003, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects, and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Programming Tutorials, which has gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

Baldwin@DickBaldwin.com

-end-