Changing Focus Traversal Keys in Java V1.4

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.

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.

Focus traversal

The focus cycle root

Focus traversal cycle

Focus owner

Up cycle and down cycle traversal operations

Focus traversal policy

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

How to control focusability at runtime.

The ability to query for the currently focused Component.

The default Focus Traversal Policy.

How to establish a focus traversal policy and modify it at runtime.

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.

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

traverse forward to the next Component: TextAreas: CTRL-TAB on KEY_PRESSEDAll others: TAB on KEY_PRESSED and
CTRL-TAB on KEY_PRESSED

traverse backward to the previous Component: TextAreas: CTRL-SHIFT-TAB on
KEY_PRESSEDAll others: SHIFT-TAB on KEY_PRESSED
and CTRL-SHIFT-TAB on
KEY_PRESSED

traverse up one focus traversal cycle : <none>

traverse down one focus traversal cycle : <none>

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 object being added must implement the Comparable interface,
or

An object of a class that implements the Comparator interface,(capable of comparing two objects of the type being added),
must be provided when the Set object is instantiated.

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.

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.

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.

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.

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.

FORWARD_TRAVERSAL_KEYS = 0

BACKWARD_TRAVERSAL_KEYS = 1

UP_CYCLE_TRAVERSAL_KEYS = 2

DOWN_CYCLE_TRAVERSAL_KEYS = 3

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

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

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

The object being added must implement the Comparable interface,
or

An object of a class that implements the Comparator interface,(capable of comparing two objects of the type being added),
must be provided when the Set object is instantiated.

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:

AbstractSet

HashSet

LinkedHashSet

TreeSet

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.

The third parameter, onKeyRelease, shouldbe 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 anyaccessibility, 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.

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.

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.

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.

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

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.

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.

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.

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.

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.

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

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.

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 typeclass javax.swing.KeyStrokeFigure 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 KeyStrokeclass 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)."

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.

//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()));}});

//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);

//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);

//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);

//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());

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

//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{

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