Published: August 5, 2003
By Richard G. Baldwin
Java Programming Notes # 1848
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.
In this lesson, I will teach you two different ways to change the focus traversal keys on an individual component at runtime.
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:
CTRL-TAB
on KEY_PRESSED
TAB
on KEY_PRESSED
and
CTRL-TAB
on KEY_PRESSED
CTRL-SHIFT-TAB
on
KEY_PRESSED
SHIFT-TAB
on KEY_PRESSED
and CTRL-SHIFT-TAB
on
KEY_PRESSED
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{ |
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{ |
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 |
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(); |
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( |
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. |
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"); |
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 |
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 = |
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 |
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 |
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( VK_ENTER The modifiers consist of any combination SHIFT_DOWN_MASK The third parameter, onKeyRelease, should |
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) |
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(){ |
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){ |
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 |
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(); |
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( |
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 |
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")){ |
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 |
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 |
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"); |
The output produced by the code in Listing 19 is shown in Figure 6.
Keystroke type |
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. |
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 |
The output produced by Listing 21 is shown in Figure 7.
Type of set |
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( |
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 |
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{ |
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 |
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 |
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( |
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 |
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. |
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"); |
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 |
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 |
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.
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.
In this lesson, I have taught you two different ways to change the focus traversal keys on an individual component at runtime.
Future lessons will discuss new focus features of version 1.4 including the following:
/* File FocusKeys01.java |
/* File FocusKeys02.java |
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.
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.
-end-