Java 2 Standard Edition, 1.4 (aka Merlin) introduces many exciting and long-awaited changes to the Java platform. One such change is to the AWT focus management subsystem. The new implementation of the AWT focus subsystem is a bold break from the old one. In fact, much of the code has been completely rewritten, in some cases at the expense of backward compatibility. Such measures were deemed necessary due to the inadequacy of the old AWT focus subsystem.
At the heart of the improved focus model is the new KeyboardFocusManager
class, bolstered by several added Swing and AWT classes. We'll spend the majority of this article learning about these changes and discussing how they might impact your Java programming efforts. At the end of the article I'll offer a few tips and a hands-on example to help you integrate your current applications with the new API.
Note that this article assumes you are familiar with the use and terminology associated with previous implementations of the AWT focus subsystem.
KeyboardFocusManager is the class that manages tasks related to focus for the new AWT focus subsystem. It is responsible for the active and focused window and the current focus owner. Its role is to enable client code to initiate focus changes and dispatch all events related to focus.
KeyboardFocusManager brings numerous new capabilities to the
AWT focus subsystem. Among them are the following:
- You can use Shift-Tab to shift focus to the previous component in the tab group.
- You can track focus traversal activity originated by the mouse.
- You can determine the current focus owner.
KeyboardFocusManager has four fields:
-
FORWARD_TRAVERSAL_KEYS: Typically the Tab (or Ctrl-Tab) key -
BACKWARD_TRAVERSAL_KEYS: Typically Shift+Tab (or Ctrl-Shift-Tab) -
UP_CYCLE_TRAVERSAL_KEYS: No default value -
DOWN_CYCLE_TRAVERSAL_KEYS: No default value
We'll discuss some of these fields in the sections that follow.
KeyboardFocusManager is an abstract class and can be used to globally request focus information. For example, KeyboardFocusManager.getFocusOwner() returns the current focus owner. The DefaultKeyboardFocusManager class is provided as the default for AWT applications. You do have the option to replace the focus model with your own KeyboardFocusManager class. Given the complexity of native focus policies, however, you're advised to stick with subclassing KeyboardFocusManager or DefaultKeyboardFocusManager.
General improvements to the AWT focus subsystem
Previous releases of the AWT focus subsystem were plagued with inconsistent behavior depending on the type of component -- lightweight or heavyweight -- and the platform hosting the Java virtual machine. Because heavyweight components render using a separate native window (AWT components), they are reliant on the native focus system. Lightweight (Swing-based) components
have the same look and feel regardless of platform. KeyboardFocusManager resolves this inconsistency, ensuring that all focus-related actions and queries are possible with both lightweight and heavyweight components.
Determining the current focus owner and the focused window
The current focus owner is a key element in the new AWT focus model. All traversal operations start from the current focus owner or from another component virtually considered as the focus owner. There can be only one current focus owner at a given time. The current focus owner is the component that has received a FOCUS_GAINED but no FOCUS_LOST event (see below for more on these events).
You use the KeyboardFocusManager.getFocusOwner() to determine the current focus owner. This method will return null if the focus owner is not in the same context as the calling thread. Therefore, you should use KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner()
in your code. This will return null only when focus has been set to "no component." Additionally, the Component class provides the isFocusOwner() method, which returns true if the component is the focus owner.
Similarly, the focused window is the window that contains the current focus owner. The KeyboardFocusManager.getFocusedWindow() returns the focused window that is in the same context as the calling thread. Again, you should use
KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow()
to ensure the focus window is in the same context as the calling thread.
Determining the opposite component in a focus change
Some window and component focus events have corresponding opposite events. These event sets include:
-
FOCUS_LOSTandFOCUS_GAINED -
WINDOW_ACTIVATEDandWINDOW_DEACTIVATED -
WINDOW_LOST_FOCUSandWINDOW_GET_FOCUS
When one of these events is dispatched, the opposite event is always dispatched simultaneously. The opposite component in a focus change is simply the component that gets the opposite event. For example, when a component gets the focus, the opposite component is the component that loses the focus.
The FocusEvent.getOppositeComponent() and
WindowEvent.getOppositeWindow() methods return the opposite component or window involved in a focus change. The six focus events listed above are closely related to
these two methods. When a window gets a WINDOW_DEACTIVATED event, the
getOppositeWindow() method looks for the window that got the WINDOW_ACTIVATED event. Each of these methods will return null if its opposite component or window is in a different context.
As defined in the AWT focus subsystem specification, a focus traversal cycle is a group of components defined such that each component in the group (and no components outside the group) will be traversed during a forward or backward focus traversal.
Each component in a focus traversal cycle has its own previous and next component. The KeyboardFocusManager class provides methods to transfer focus in a given traversal cycle or even change focus traversal cycles.
focusNextComponent() transfers focus to the next component in the traversal cycle. This method can take another component as its argument, and will then transfer focus to the component that follows in the focus traversal cycle. focusPreviousComponent() works similarly for the previous component in the traversal cycle.
A Container generally functions as both a member of a focus traversal cycle and the root of another focus traversal cycle. This brings us to the concept of focus traversal hierarchy, and therefore of up and down focus cycles. upFocusCycle() moves the focus up one focus traversal cycle from the current focus owner. It can take a component as its argument, thus moving the focus up one traversal cycle to the component's traversal cycle. downFocusCycle()
works the same way, but only where the component is the root of a focus traversal cycle.
In the code sample below, you can see that a frame contains two components; calling upFocusCycle() from one of these two components would give the focus to the frame. Note that the focus owner will not change if there is no other component on which to focus.
You can set the focus owner to "no component" by calling KeyboardFocusManager.clearGlobalFocusOwner(). This results in a FOCUS_LOST event for the current focus owner. All key events are discarded until user interaction or code explicitly sets focus ownership to a particular component. This is valid not only for Java components, but at the native level as well.
New methods in the Component, Container, and Window classes
New focus-related methods have been added to the Component and Container classes in Merlin. You can use them as alternatives to the methods in KeyboardFocusManager to set or get focus properties for
instances of those classes. For example, Component.isFocusable() relays whether a component is focusable. The default return value is true for all components, because all components are focusable by default. This is different from previous releases where lightweight components were not focusable by default. Component.setFocusable(boolean) sets whether a component is focusable.
setFocusable(false) will result in the failure of subsequent
requestFocus() and requestFocusInWindow()
calls. Note that some methods in the Component and Container classes and in KeyboardFocusManager
have the same function and are equivalent. For example, Component.setFocusTraversalKeys() can be used to override focus traversal keys for a specific component in an application that has its own policy and focus traversal key
set defined by KeyboardFocusManager.setDefaultFocusTraversalKeys().
Window.setFocusableWindow(boolean) allows you to programmatically prevent a window or any of its subcomponents from becoming the focus owner. All windows are focusable by default, but it is necessary to render a window not focusable in certain
cases. An obvious example is the input method composition window. Input methods are used to input text characters that couldn't all be inserted in a standard 102-key keyboard. Clearly, you want the input method composition window (shown below) to be left out of the focus
traversal cycle, hence the use of Window.setFocusableWindow(false).
Figure 1. Screenshot of an input method composition window on Turbolinux Chinese

The new traversal policy classes
The order in which components are traversed is defined by a FocusTraversalPolicy class. This class should provide different methods to determine which are the previous, next, first, last, and default components. It is responsible for dispatching key events, focus events, and focus-related window events. Java.awt.FocusTraversalPolicy is the abstract class for all traversal policy classes. It has the following required methods, which can be overridden by subclasses:
-
getComponentAfter() -
getComponentBefore() -
getDefaultComponent() -
getFirstComponent() -
getLastComponent()
AWT provides the non-abstract ContainerOrderFocusTraversalPolicy,
whose methods return only components that are visible, displayable, enabled, and
focusable. DefaultFocusTraversalPolicy subclasses
ContainerOrderFocusTraversalPolicy. It will never return
components with non-focusable peers and is the default policy for all AWT containers (Panel, Window, and ScrollPane). Swing provides an interesting additional policy,
LayoutFocusTraversalPolicy, which sorts component into rows and columns based on their geometrical properties.
Note that creating your own focus traversal policy is a complex task. Rather than designing a policy from scratch, you're advised to use the existing default policy (which should fulfill most requirements), overriding some methods to fit your specific needs. The new focus policy classes in Merlin have been designed with this purpose in mind.
If you have been using Swing's DefaultFocusManager
class, you should use the new Merlin FocusTraversalPolicy instead,
as strongly recommended in the API documentation. (See Resources for a link to the documentation).
Following are a few simple -- but essential -- changes that will let you make the most of the improved AWT focus subsystem.
Use Component.requestFocusInWindow()
Because certain platforms allow for focus change across windows and
others do not, Component.requestFocus() is inconsistent across platforms. Component.requestFocusInWindow resolves this by rendering
cross-window focus traversal impossible on any platform. Moreover, requestFocusInWindow() returns a boolean value: false means the focus request will fail; true means it is likely to pass. Note that you shouldn't assume a component has received focus after a call to requestFocus() or requestFocusInWindow(). Install a focus listener on the component and look for the FOCUS_GAINED event.
Use java.awt.KeyboardFocusManager
If you've used javax.swing.FocusManager
to gain a global view on focus changes for Swing components, you've likely noticed its limitations. FocusManager doesn't see focus changes initiated by the mouse and, more importantly, it works only for Swing components. Because KeyboardFocusManager is an AWT class, it sees what's happening for all components, not just those pertaining to Swing. In addition, KeyboardFocusManager features an improved API.
Because AWT follows the requirements of the native platform, the behavior of applications can be different on different platforms.
For example, let's imagine you defines a class, myComponent, that extends Component and overrides the isFocusTraversable() method so that it returns false. In other words,
any instance of this class won't be focus traversable. Now, let's imagine an application adds an instance of myComponent to a frame, plus other components (in this order) for which the isFocusTraversable() method isn't overridden. Which component gets the initial focus when the application is started? It isn't clearly defined in the documentation.
Although it makes sense for the next focus traversable visible component to get the initial focus, it really depends on the native focus manager. On some platforms, you will see the behavior described above, but it can also happen that no component gets the focus. This lead to the redefinition of isFocusTraversable() from "can be traversed using Tab or Shift-Tab" to "can be focused." This is only one of numerous examples where AWT has relied too heavily on the native focus manager. Even as AWT's behavior becomes increasingly consistent in new releases, Swing will better ensure cross-platform consistency.
How to use the new API: A working example
We'll close with a live code sample. The listing below uses the new
classes and methods described in this article. The TextField has focus listeners to demonstrate the focus changes to and from the example component. When you run the program (zipped in Resources), you can see that TextField gets the initial focus. At the end,
after the call to clearGlobalFocusOwner(), no component has the focus.
Use the example below along with the descriptions and discussion in this article as a starting point to learning about the new AWT focus subsystem. See Resources for relevant documentation and further reading.
Live code sample
import java.awt.*;
import java.awt.event.*;
public class MerlinFocus extends Frame implements FocusListener {
DefaultKeyboardFocusManager myFocusmgr
= new DefaultKeyboardFocusManager();
Button bt;
TextField tf;
MerlinFocus() {
bt = new Button("myButton");
tf = new TextField("myTextField");
tf.addFocusListener(this);
setLayout(new GridLayout());
add(tf);
add(bt);
}
/* Focus Listeners */
public void focusLost(FocusEvent e) {
System.out.println("myTextField: Focus lost");
System.out.println("The opposite component "
+ "in this focus traversal is "
+ e.getOppositeComponent()+".");
}
public void focusGained(FocusEvent e) {
System.out.println("myTextField: Focus gained");
}
public void printDefaultSettings(DefaultKeyboardFocusManager fm) {
/* print the four default traversal keys */
System.out.println("Default keys "
+ "for the DefaultFocusManager class:");
System.out.println(" FORWARD_TRAVERSAL_KEYS: "
+ fm.getDefaultFocusTraversalKeys(
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
System.out.println(" BACKWARD_TRAVERSAL_KEYS: "
+ fm.getDefaultFocusTraversalKeys(
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
System.out.println(" UP_CYCLE_TRAVERSAL_KEYS: "
+ fm.getDefaultFocusTraversalKeys(
KeyboardFocusManager.UP_CYCLE_TRAVERSAL_KEYS));
System.out.println(" DOWN_CYCLE_TRAVERSAL_KEYS: "
+ fm.getDefaultFocusTraversalKeys(
KeyboardFocusManager.DOWN_CYCLE_TRAVERSAL_KEYS));
}
public void changeFocusOwner(DefaultKeyboardFocusManager fm) {
/* programmatically change the focus owner */
System.out.println("The current focus owner is "
+ fm.getFocusOwner());
System.out.println("The current focus cycle root is: "
+ fm.getCurrentFocusCycleRoot());
System.out.println("forwarding focus to the next component...");
fm.focusNextComponent();
System.out.println("Now, the focus owner is "
+ fm.getFocusOwner());
System.out.println("going up one focus cycle...");
fm.upFocusCycle();
System.out.println("Now, the focus owner is "
+ fm.getFocusOwner());
System.out.println("going down one focus cycle...");
fm.downFocusCycle();
System.out.println("Now, the focus owner is "
+ fm.getFocusOwner());
System.out.println("clearing the global focus owner ...");
fm.clearGlobalFocusOwner();
System.out.println("Now, the focus owner is "
+ fm.getFocusOwner());
}
public static void main(String args[]) {
MerlinFocus mf = new MerlinFocus();
mf.setTitle("Merlin Focus");
mf.pack();
mf.setVisible(true);
mf.printDefaultSettings(mf.myFocusmgr);
mf.changeFocusOwner(mf.myFocusmgr);
}
}
|
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample code | j-awtfocus.zip | 1 KB | HTTP |
Information about download methods
- See the official Sun Microsystems API specification to learn more about Merlin (Java 2 Platform, Standard Edition, v1.4).
- You will likely also want to browse the formal
documentation for the new AWT focus subsystem.
- See John Zukowski's regular developerWorks column,
Magic with Merlin
,
to get up to speed quickly with the latest implementation of the Java 2 Platform, JDK 1.4.
- You'll find all the IBM developer kits and run-time environments available for download on the IBM developer kit page.
- Find more Java resources on the developerWorks Java technology zone.
Bertrand Portier graduated with a Diplome d'Ingenieur in Computer Engineering from The University of Lille Polytechnic School, France, in 2000. He now works as a software engineer at the IBM Centre for Java Technology in Hursley, England, on Java User Interface technology. Contact Bertrand at portier@uk.ibm.com.




