Skip to main content

Java 2 gets a new focus subsystem

What's new in JDK 1.4 and how to use it

Bertrand Portier (portier@uk.ibm.com), Software engineer, IBM
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.

Summary:  The Java 2 platform gets a completely new AWT focus subsystem with the latest release of Java Standard Edition, 1.4, and we've got the scoop on what makes it go. Java language engineer Bertrand Portier offers a first look at the new classes and methods (including the essential KeyboardFocusManager) that comprise the AWT focus subsystem, tips for adapting your programming efforts as you migrate to the new API, a working code example, and more.

Date:  01 Oct 2001
Level:  Intermediate
Activity:  3241 views

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.

Intro to KeyboardFocusManager

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_LOST and FOCUS_GAINED
  • WINDOW_ACTIVATED and WINDOW_DEACTIVATED
  • WINDOW_LOST_FOCUS and WINDOW_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.


Programmatic focus traversal

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
Screenshot of an input method composition window on Turbolinux ChineseScreenshot 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).


Migration tips

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.

Use Swing components

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

       }
}



Download

DescriptionNameSizeDownload method
Sample codej-awtfocus.zip1 KB HTTP

Information about download methods


Resources

About the author

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.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=10605
ArticleTitle=Java 2 gets a new focus subsystem
publish-date=10012001
author1-email=portier@uk.ibm.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers