Focus on Swing

Explore focus management with JFC's Swing components

Focus is the mechanism that determines which of the components in a window will receive keyboard input events. A focus manager looks for special keystrokes that change the focus (usually the Tab and Shift-Tab keys), and then decides which component will next get the focus. Often, you'll want to control how the focus moves between components, especially if you're designing a complex form or dialog for users to enter information.

Java has had a focus manager for AWT components since version 1.0 of the Java Development Kit (JDK), and developers have howled about its inadequacies ever since. Well, it's time to stop howling. The focus management services provided with the Java Foundation Classes (JFC) should satisfy all of your needs for controlling focus changes.

A bit of history

The only way to manage focus changes with JDK 1.0 is through a few methods in the Component class. JDK 1.0 allows components to set the focus to themselves with requestFocus and to change the focus to the next component with nextFocus. Other than getting notification of focus changes with gotFocus and lostFocus, that's pretty much all you can do.

JDK 1.1 deprecates nextFocus and replaces it with transferFocus, which provides exactly the same functionality as nextFocus. Likewise, 1.1 deprecates gotFocus and lostFocus and replaces them with the addFocusListener method to support focus change notification using the 1.1 event model.

Neither JDK 1.0 nor 1.1 provide any means for applications to override the behavior of the focus manager, or to change the algorithm used to determine focus traversal.

What's new with Swing's focus management

Swing provides some powerful new ways to manage focus changes at the component level. Here's what you can do with focus management at that level:

  • Set the focus to a given component
  • Transfer the focus to the next component in the focus-change order
  • Determine if a component has the focus
  • Disable a component from responding to focus requests
  • Allow the component that currently has the focus to manage the next focus change

And if this isn't enough to meet your focus control needs, Swing allows you to create and use your own custom focus manager.

Let's take a quick look at the relationship between the AWT and Swing focus managers. The following figure shows how JFC incorporates both the AWT and Swing focus managers.

AWT and Swing focus manager relationship

Note that the AWT focus manager will manage focus for Swing components because they are extended AWT components. However, the flip side is not true: The Swing focus manager will not manage focus for AWT components. The section "Mixing AWT and Swing components" explains how to handle focus management if you're mixing these two component types in an application.

Explicitly setting the focus

There are two JComponent methods that allow you to explicitly set the focus to a given component: requestFocus and grabFocus. If you've worked with AWT components, you're probably familiar with the requestFocus method. Swing overrides this Component method with an implementation that allows you to call the new setRequestFocusEnabled method. This method can enable or disable the component from getting the focus via requestFocus.

However, you can't use setRequestFocusEnabled to prevent a component from getting the focus via the focus manager. That's because the focus manager uses a new Swing method, grabFocus, to give components the focus. grabFocus works just like requestFocus, except that you can't disable it. The Swing docs say that only focus managers should use grabFocus.

Let's take a look at a bit of sample code written for components. This code will cause a component to get the focus whenever the component is under the mouse:

// add a listener for mouse enter
addMouseListener(new MouseAdapter() {
    public void mouseEntered(MouseEvent event) {
        // Request the focus (if don't already have it)
        if(!hasFocus()) { requestFocus(); }
    }
});

This fragment adds a mouse listener, which is implemented in an anonymous inner class based on the MouseAdapter adapter class. The mouse adapter's mouseEntered method is called whenever the mouse passes over the component. The implementation for mouseEntered calls hasFocus to see if the component already has the focus, and if it doesn't, calls requestFocus to give the component the focus. A slightly more fail-safe way to set the focus would be:

if(!hasFocus()) {
    // ensure requestFocus is enabled
    if(!isRequestFocusEnabled()) { setRequestFocusEnabled(true); }
    requestFocus();
}

And if you're writing a focus manager, you can use

grabFocus:

if(!hasFocus()) { grabFocus(); }

Transferring the focus

Like AWT 1.1, Swing provides the means for a component to transfer the focus to the next component with the transferFocus method.

public void transferFocus();

This method can be useful for implementing components that automatically advance the focus when the user has completed whatever task or purpose is served by the component (like choosing from a group of radio buttons or items in a list). transferFocus is not really useful for explicitly setting the focus -- you would have to know which component has the focus just before the component that you actually want to set the focus to.

Detecting focus changes

Swing doesn't bring any changes to the operation of AWT 1.1's focus listener implementation. I'll give a brief description of focus listeners for those readers that aren't familiar with them; if you know this stuff you may want to skip on to the next section.

Focus listeners provide a means of notifying applications when components gain or lose the focus. The adapter class for focus events, FocusAdapter, has two methods: focusGained and focusLost. The addFocusListener and removeFocusListener methods add and remove focus listeners for components. Standard event-listener stuff -- nothing fancy here. Let's glance at a piece of code. The following code fragment adds a focus listener that changes the background color when a component gets the focus.

// private instance variables
private boolean bFocusIndicated = false;
private Color originalColor;
.
.
.
// add a listener for focus events
addFocusListener(new FocusAdapter() {
    public void focusGained(FocusEvent e) {
        if(!bFocusIndicated) {
            // set background to a darker color to indicate focus
            originalColor = getBackground();
            setBackground(originalColor.darker());
            bFocusIndicated = true;
        }
    }
    public void focusLost(FocusEvent e) {
        // restore original background color
        setBackground(originalColor);
        bFocusIndicated = false;
    }
});

You can't always count on getting focusGained and focusLost events in matched pairs, hence the need for the bFocusIndicated variable to keep up with whether we've indicated focus on a component.

Managing focus at the component level

Up until this point, most of what I've covered is probably familiar to you if you have much experience with AWT 1.1 components. Now we'll start getting into Swing's new and improved features.

Swing provides a mechanism for component-managed focus traversal. This can be a powerful alternative to writing your own focus manager and is a better solution for developing components that have their own particular behavior when it comes to gaining and losing the focus. For example, say you want to develop a text field component that won't surrender the focus until the user enters valid data. If you've tried to implement such a component in AWT 1.1 by using the focusLost notification to validate the data and then set the focus back to the text field, you've probably experienced some frustration. One obvious solution to this problem -- calling requestFocus while processing the focusLost event -- doesn't always have a predictable outcome.

With Swing components, you can override the isManagingFocus method to return true and get Tab and Shift-Tab keystrokes sent to the component's key listener. This allows the component to manage focus traversal when the component has the focus. The following code implements a text field that doesn't allow the focus to traverse to the next component until valid data is entered:

class MyTextField extends JTextField {
    boolean validDataEntered = false;
    // Constructors
    public MyTextField () {
        super();
        init();
    }
    public MyTextField (String text) {
        super(text);
        init();
    }
    // Private initialization routine to be run at construct time
    private void init() {
        // add a key event listener that will consume tab keys until valid data entered in field
        addKeyListener(new KeyAdapter() {
            public void keyPressed(KeyEvent event) {
                // look for tab keys
                if(event.getKeyCode() == KeyEvent.VK_TAB
                || event.getKeyChar() == '\t') {
                    // if no valid data entered in field, consume event
                    // so that it won't be passed on to focus manager
                    if(!validDataEntered) {
                        event.consume();
                    }
                }
                else {
                    // assume any key other than tab is valid data
                    validDataEntered = true;
                }
            }
        });
    }
    // Override to inform focus manager that component is managing focus changes
    public boolean isManagingFocus() { return true; }
}

In this example, any keystrokes other than Tab keystrokes are considered to be valid data. If no valid data has been entered, the text field's key listener consumes Tab keystrokes, preventing them from reaching the focus manager. Of course, if you were implementing this technique in a real application, you would want to present a message to the user and inform them as to why they are being prevented from tabbing away from the text field.

One limitation to managing focus traversal at the component level is that you can't control focus changes initiated by mouse actions. In fact, you can't even do this by writing your own custom focus manager. To control focus changes initiated by mouse actions, you'll have to modify the low-level mouse handling for all of your components.

Mixing AWT and Swing components

The Swing focus manager effectively sits on top of the AWT focus manager. It can only manage focus for Swing components, so if you're mixing AWT and Swing components, you should disable the Swing focus manager with the following line of code.

FocusManager.disableSwingFocusManager();

The AWT focus manager will then manage focus for all of your components. Unfortunately, you can't disable the focus manager on a per-window or per-container basis -- when you disable it, it's disabled for your entire application.

Writing a custom focus manager

Swing provides the ability for applications to install and use their own custom focus manager. If you can't get the focus traversal behavior you want by using requestFocus and transferFocus, or by managing focus at the component level, you might want to consider writing a custom focus manager. Although Swing does provide a complete, clean structure for supporting custom focus managers, writing a robust focus manager that works well with any arbitrary component hierarchy is no trivial task.

Overriding the behavior of the default focus manager (represented by the DefaultFocusManager class) may look like a promising and cheap way to create a custom focus manager. I don't recommend this approach. In my opinion, the default focus manager isn't really designed to be extended. For example, you might think you can just override the compareTabOrder method and implement any tabbing algorithm you like. That would be nice, but it doesn't quite work that way -- the default focus manager makes certain assumptions about the tabbing order before it even calls compareTabOrder. If you try to customize the default focus manager, I guarantee you will spend a lot of time in the sources trying to understand exactly how everything works.

The structure of a focus manager

Let's take a look at the structure of a Swing focus manager. Focus managers are based on the abstract FocusManager class. This class contains four static class methods for applications to use to install and manage focus managers.

  • getCurrentManager
  • setCurrentManager
  • disableSwingFocusManager
  • isFocusManagerEnabled

The remaining three methods in the class are abstract methods that focus managers must implement:

  • processKeyEvent
  • focusNextComponent
  • focusPreviousComponent

That's right -- there are only three methods you must implement to create a custom focus manager. Two of the methods, focusNextComponent and focusPreviousComponent, simply advance the focus to the next or previous component. The other method, processKeyEvent, is a low-level handler for key events. This method looks for focus-change keystrokes (normally Tab and Shift-Tab) and then calls focusNextComponent or focusPreviousComponent to change the focus.

The static class methods getCurrentManager, setCurrentManager, disableSwingFocusManager and isFocusManagerEnabled give you access to the current focus manager and allow you to disable the Swing focus management services as well as install your own focus manager.

The abstract methods processKeyEvent, focusNextComponent, and focusPreviousComponent are methods you must override and implement if you are writing a focus manager.

Example of a custom focus manager

Let's take a look at an example of a custom focus manager. This example application, called "CustomFocusManagerExample," implements a focus manager that allows you to specify the tab order of components, regardless of their screen position or the order in which they were added to their container. The custom focus manager used in this example determines focus changes by querying the currently-focused component for the next or previous component to get the focus. Components managed by this focus manager must implement methods for setting and getting the next and previous focus recipients.

There are four parts to this example:

  • A custom focus manager, represented by the CustomFocusManager class.

  • An interface class that components managed by this custom focus manager must implement. The interface class is represented by the CustomFocusManagerSupport class.

  • A component that implements methods required by the custom focus manager. This component is represented by the MyTextField class, an extension of JTextField.

  • The application shell to illustrate use of the custom focus manager. The application shell consists of the CustomFocusManagerExample and AppPanel classes. It's designed to be run as an applet or as a standalone application.

The application creates six text fields identified by a number from one to six. Instead of adding the text fields to their container in numerical order, it adds them in a somewhat arbitrary order: one, six, three, five, two, four. The application then sets the tab order such that focus traverses the text fields in numerical order: one, two, three, four, five, six. Here's a screen shot of the application when it's run as a standalone application.

Screen shot of CustomFocusManagerExample application

Now let's get into the implementation. We'll start with the interface that components must implement to be managed by our custom focus manager.

interface CustomFocusManagerSupport {
    public void setNextFocus(JComponent component);
    public void setPreviousFocus(JComponent component);
    public JComponent getNextFocus();
    public JComponent getPreviousFocus();
}

The interface is pretty simple -- just a few methods to set and get focus-traversal information. The MyTextField class implements this interface by adding two private instance data members to store the next and previous components to get the focus.

class MyTextField extends JTextField implements CustomFocusManagerSupport {
private JComponent nextFocus = null;
private JComponent previousFocus = null;
// Constructors
...
// Implementation of CustomFocusManagerSupport interface
public void setNextFocus(JComponent component) { nextFocus = component; }
public void setPreviousFocus(JComponent component) { previousFocus = component; }
public JComponent getPreviousFocus() { return (previousFocus); }
public JComponent getNextFocus() { return (nextFocus); } 
}

Now let's look at the code for the custom focus manager. This example focus manager is implemented in a class named CustomFocusManager, an extension of the FocusManager class. Two of the abstract methods that focus managers must implement are focusNextComponent and focusPreviousComponent. Here's how we implement these methods in the CustomFocusManager class.

public void focusNextComponent(Component component) {
    if(component instanceof CustomFocusManagerSupport) {
        CustomFocusManagerSupport focusedComponent = (CustomFocusManagerSupport) component;
        if (focusedComponent.getNextFocus() != null) {
            focusedComponent.getNextFocus().grabFocus();
        }           
    }
}
public void focusPreviousComponent(Component component) {
    if(component instanceof CustomFocusManagerSupport) {
        CustomFocusManagerSupport focusedComponent = (CustomFocusManagerSupport) component;
        if (focusedComponent.getPreviousFocus() != null) {
            focusedComponent.getPreviousFocus().grabFocus();
        }           
    }
}

Nothing fancy here either -- we just check to see if the given component implements the CustomFocusManagerSupport interface and call getPreviousFocus or getNextFocus to get the component that should next get the focus. We then set the focus to that component by calling its grabFocus method.

Before I show you how the focus manager processes keystrokes, let's stop for a moment and think about the focusNextComponent and focusPreviousComponent methods. Remember, these are abstract methods that all focus managers must implement. Does it seem odd that these methods have a parameter specifying a component? When you tell the focus manager to advance the focus, you must give it the component that should have the focus before the component you want to advance it to. I know this seems convoluted, but it makes sense when you understand that the focus manager doesn't keep track of which component currently has the focus. Focus management is a cooperative effort between components and the focus manager.

The final part of the custom focus manager code is the implementation of the processKeyEvent method. This is the part of the focus manager that actually handles the special keystrokes that change the focus.

public void processKeyEvent(Component component, KeyEvent event) {
    if(component instanceof CustomFocusManagerSupport) {
        // look for tab key
        if(event.getKeyCode() == KeyEvent.VK_TAB || event.getKeyChar() == '\t') {
            if(event.getID() == KeyEvent.KEY_PRESSED){
                // is shift pressed?
                if ((event.getModifiers() & ActionEvent.SHIFT_MASK) == ActionEvent.SHIFT_MASK) {
                    focusPreviousComponent(component);
                }
                else {
                    focusNextComponent(component);
                }
            }
            // consume all tab key events
            event.consume();
        }
    }
}

The processKeyEvent method looks for Tab and Shift-Tab keystrokes and then calls focusNextComponent and focusPreviousComponent to change the focus. It only handles focus for components that implement the CustomFocusManagerSupport interface. Note that focus changes occur only on the KEY_PRESSED event -- processKeyEvent consumes corresponding KEY_RELEASED and KEY_TYPED events by calling the KeyEvent.consume method.

The remaining piece of the puzzle is how the application installs a custom focus manager. This is accomplished by creating an instance of the custom focus manager and setting it as the current focus manager by calling the FocusManager class method setCurrentManager.

FocusManager fm = new CustomFocusManager();
FocusManager.setCurrentManager(fm);

Conclusion

You can handle the majority of your focus management needs with Swing components by using JComponent methods like requestFocus, transferFocus, and addFocusListener. If these methods don't do the trick, try managing focus at the component level by overriding isManagingFocus and adding a key listener to listen for focus-change keystrokes. If this approach is still not sufficient, or if you need a more general solution for focus management, you can write a custom focus manager.

In addition to software engineering, Mark McCulley has worked in the fields of computer hardware engineering and technical writing. Always on the lookout for easier and better ways to do things, he began learning Java in early 1997. Mark is especially interested in music and multimedia software and created the first commercially available MIDI sequencer for the Atari ST computer in 1986. He's currently authoring a book on the Java Foundation Classes.

Learn more about this topic

Join the discussion
Be the first to comment on this article. Our Commenting Policies