Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Magic with Merlin: Focus, focus, focus

Focus management system reworked

John Zukowski (jaz@zukowski.net), President, JZ Ventures, Inc.
Author photo
John Zukowski conducts strategic Java consulting with JZ Ventures, Inc. and is working with SavaJe Technologies to develop a next-generation mobile phone platform. His latest books are Java Collections (APress, April 2001) and Definitive Guide to Swing for Java 2, Second Edition (Apress, January 2000). Reach him at jaz@zukowski.net.

Summary:  Focus is the art of managing what component receives keyboard input and when it receives it. In past versions of J2SE, this feature didn't always work according to plan. In this month's Magic with Merlin, John Zukowski explores one of Merlin's many changes to remedy earlier focus problems.

View more content in this series

Date:  15 Jul 2003
Level:  Introductory
Also available in:   Japanese

Activity:  5283 views
Comments:  

Some of the lingering issues with Swing-based GUIs include how to manage focus (which component has priority to receive keyboard input), determining what component has it, and how to traverse from one component to the next. Because Swing is built on top of the Abstract Window Toolkit (AWT), the management of component focus relies on the underlying focus management in the AWT. Past releases of the Java platform relied on the native window manager to assist with focus management, so while developers might have thought focus control was within their Java application, that wasn't always the case. Because of the reliance on the underlying native focus system, numerous platform "inconsistencies" appeared.

With Merlin, you get a whole new focus subsystem at the AWT level. The subsystem has its good and bad points. The new model is meant to create a workable cross-platform system, with a centralized KeyboardFocusManager to manage the active and focused windows, as well as the current focus owner. The downside is that there are some incompatibilities with prior releases, causing some programs not to work well with the newer release. As a developer, you need to be aware of the new focus traversal behaviors when you create any new programs.

The new focus subsystem is quite large, and in this tip, we'll highlight just one of the new features -- the FocusTraversalPolicy -- and show you how to manage focus traversal within a single container. For information on the remaining features, see Resources for links to Sun documentation and other important guides.

What, no interfaces?

We'll start by looking at the FocusTraversalPolicy class. Yes, it is a class, not an interface. It is abstract, though, so it is meant to be subclassed. The FocusTraversalPolicy class controls the order of traversal within a particular focus cycle root. A focus cycle root is a container whose focusCycleRoot property is set to true. By default, windows and frames are set to true and remaining containers are set to false, but can be made true. Setting the property to true means that when focus moves back and forth, the focus will always stay within a component of the cycle within its focus cycle root.

The FocusTraversalPolicy class consists of six methods:

  • getDefaultComponent(Container focusCycleRoot)
  • getInitialComponent(Window window)
  • getComponentBefore(Container focusCycleRoot, Component aComponent)
  • getComponentAfter(Container focusCycleRoot, Component aComponent)
  • getFirstComponent(Container focusCycleRoot)
  • getLastComponent(Container focusCycleRoot)

All six methods return a Component object. Of the six methods, five are abstract, with getInitialComponent() being the only concrete method.

As the name suggests, the getDefaultComponent() method returns the default component that should get focus when the associated focus cycle root gets focus. Imagine tabbing around a container and tabbing into a container within that container. This is called a down focus cycle. When focus enters that subcontainer, getDefaultComponent() needs to return the initial component that should get focus.

The getInitialComponent() method returns the initial component that should get focus when a window is first displayed. By default, this method just returns the default component for the window -- the result of calling getDefaultComponent().

The getComponentBefore() and getComponentAfter() methods work as a pair. Given a particular component in a specific focus cycle root (think container), the methods return which component would be before it or after. Typically, you press Shift+Tab to move backward to a previous component and press Tab to advance forward to the next component, but circumstances may warrant having different key sequences to move focus with the keyboard. Typically, these methods are written as one large if-else block or a Map lookup.

The getFirstComponent() and getLastComponent() methods are also a pair. While logically you should write getComponentBefore() and getComponentAfter() with a first/last component in mind, the initial component doesn't have to be first and these methods let you explicitly set which components are first and last.


Built-in policies

The system includes five built-in focus traversal policies, the latter three of which are specific to Swing:

  • ContainerOrderFocusTraversalPolicy
  • DefaultFocusTraversalPolicy
  • InternalFrameFocusTraversalPolicy
  • SortingFocusTraversalPolicy
  • LayoutFocusTraversalPolicy

ContainerOrderFocusTraversalPolicy works by using the container's implicit component ordering (its getComponents() array), which is typically the order in which components are added to the container -- unless you add() components at a specific position. In addition to the six methods of FocusTraversalPolicy, ContainerOrderFocusTraversalPolicy also supports implicitly transferring focus down into a container and offering an accept() method that you can override to define what is an acceptable choice for a component getting focus.

DefaultFocusTraversalPolicy works very much like ContainerOrderFocusTraversalPolicy, with one exception -- it relies on the AWT's peer component to check on the focusability of a component. The focusability of a peer is implementation dependent and returns you to the problems of the existing focus management subsystem. When you work with Swing GUI components, you don't have to worry about a peer's focusability.

InternalFrameFocusTraversalPolicy is the policy for JInternalFrame. Given that a JInternalFrame is a not a heavyweight window, it needs special behavior to deal with when it is first shown.

SortingFocusTraversalPolicy allows you to define a Comparator to control the focus traversal policy. Instead of using the deep if-else block or lookup Map, create a comparator to deal with the ordering.

There is also a specialized SortingFocusTraversalPolicy that provides a default Comparator: LayoutFocusTraversalPolicy. Here, components are sorted by size, position, and orientation, instead of the order in the getComponents() array.

A sixth policy is also available, but it isn't public: LegacyGlueFocusTraversalPolicy. This policy is used when old code uses something like JComponent.setNextFocusableComponent().


A working example

Next, we'll create a simple example that demonstrates the use of our own focus traversal policy. We'll create a FocusTraversalPolicy implementation that takes an array and relies on the component order in the array to determine traversal order. The sample window will be the typical BorderLayout example where the components show off where North, South, East, West, and Center are located. The traversal order will go clockwise around the outside before visiting the middle. Figure 1 shows the program:


Figure 1. A BorderLayout window
A BorderLayout window

Listing 1 shows the full clockwise traversal listing:


Listing 1. Clockwise traversal
import java.awt.*;
import javax.swing.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

public class BorderFocus {
  public static void main(String args[]) {
    JFrame frame = new JFrame("Focus Cycling");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    Container contentPane = frame.getContentPane();
    JButton north = new JButton("North");
    contentPane.add(north, BorderLayout.NORTH);
    JButton south = new JButton("South");
    contentPane.add(south, BorderLayout.SOUTH);
    JButton east = new JButton("East");
    contentPane.add(east, BorderLayout.EAST);
    JButton west = new JButton("West");
    contentPane.add(west, BorderLayout.WEST);
    JButton center = new JButton("Center");
    contentPane.add(center, BorderLayout.CENTER);
    contentPane.setFocusable(false);
    final Component order[] = 
      new Component[] {north, east, south, west, center};
    FocusTraversalPolicy policy = new FocusTraversalPolicy() {
      List list = Arrays.asList(order);
      public Component getFirstComponent(Container focusCycleRoot) {
        return order[0];
      }
      public Component getLastComponent(Container focusCycleRoot) {
        return order[order.length-1];
      }
      public Component getComponentAfter(Container focusCycleRoot, 
          Component aComponent) {
        int index = list.indexOf(aComponent);
        return order[(index + 1) % order.length];
      }
      public Component getComponentBefore(Container focusCycleRoot, 
          Component aComponent) {
        int index = list.indexOf(aComponent);
        return order[(index - 1 + order.length) % order.length];
      }
      public Component getDefaultComponent(Container focusCycleRoot) {
        return order[0];
      }
    };
    frame.setFocusTraversalPolicy(policy);
    frame.pack();
    frame.show();
   }
}

As an additional exercise, you might want to rewrite the example to use SortingFocusTraversalPolicy. Be sure to try out both forward and reverse traversal through the container.


Summary

The focus subsystem in the 1.4 release of the Java platform has remedied a number of focus-related issues in prior releases. The FocusTraversalPolicy is just one of the improvements. Be sure to read the Focus specification referenced in the Resources section to find out about the other parts of the specification, including the new KeyboardFocusManager, the differences between requestFocus() and requestFocusInWindow, and adjusting a component's focus traversal keys.


Resources

About the author

Author photo

John Zukowski conducts strategic Java consulting with JZ Ventures, Inc. and is working with SavaJe Technologies to develop a next-generation mobile phone platform. His latest books are Java Collections (APress, April 2001) and Definitive Guide to Swing for Java 2, Second Edition (Apress, January 2000). Reach him at jaz@zukowski.net.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

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=10840
ArticleTitle=Magic with Merlin: Focus, focus, focus
publish-date=07152003
author1-email=jaz@zukowski.net
author1-email-cc=jaloi@us.ibm.com

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.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

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

Special offers