Java Solaris Communities About Sun How to Buy United States Worldwide

Accessibility

Accessibility Resources Banner
Begin Product Tab Sub Links At a Glance Articles Active Sub Link Documentation

Developing Accessible JFC Applications

Jeff Dunn, June 2000

These short guidelines aggregate many of the main points in IBM's Guidelines for Writing Accessible Applications. IBM's guidelines are useful when developing complex custom components.

Good human interface and software design can make nearly any software usable by persons with disabilities. Such software is termed accessible, and the Java Foundation Classes (JFC) make implementing accessibility very easy by providing built-in support for the assistive software and technologies used by those with disabilities. It is important to understand how these assistive technologies interact with mainstream applications and what JFC/Swing! developers must do in order to provide the maximum benefit for those with disabilities.

If you already have knowledge of assistive technologies and want specifics regarding your Java application, you may want to skip the next few paragraphs. Beginning in the section titled Nuts and Bolts you'll find lots of concrete information and code samples.

There are many reasons why software should be made accessible. Of course, it's the "right thing to do" because over 40 million Americans have disabilities, and they have as much (and possibly more) need to use software as the population as a whole. Business factors must also be considered, and the strongest issues involve the Americans with Disabilities Act of 1990 and Section 508 of the Federal Rehabilitation Act.

The Americans with Disabilities Act of 1990 (ADA) requires employers to make reasonable accommodations for employees with disabilities. As the world has computerized, "reasonable accommodations" has been interpreted to include providing accessible software. This means that a company's internal applications should work well with assistive technologies and that commercial applications used by the employee must work properly with his or her assistive technology (or technologies).

Section 508 of the Federal Rehabilitation Act requires that Federal agencies' electronic and information technology be accessible to people with disabilities, including employees and members of the public. This is a new Act that will be in full force by August of 2000, and its expected interpretation is that federal agencies cannot purchase nor develop software or computer equipment that is not usable by individuals with disabilities. If your software is not accessible, you probably will not be eligible for government contracts.

Contents:

Disability - An Overview

Defining what constitutes a disability is a very complex issue, so the term will be greatly simplified here. When developing software to aid the greatest number of people, disabilities can be broadly categorized into sensory impairments and motor skill impairments. At a minimum, developers should be aware of these disabilities:

  • Color Blindness
  • Poor Vision
  • Lack of Vision
  • Poor Hearing
  • Lack of Hearing
  • Poor Mouse and Keyboard Use
  • Lack of Mouse and Keyboard Use

If an application is sufficient for these populations, it will work very well for most disabilities. However, it is important to realize that a significant number of people have multiple disabilities, and a solution should not be developed that aids one disability group while precluding alternatives for any other group.

Cognitive disabilities also deserve consideration, but they will not be addressed directly here. This is because these types of disabilities impair a person's senses in very complex ways, making the impairments difficult to quantify. However, achieving accessibility for the list of disabilities provided above will often allow a person with cognitive disabilities to make effective use of an application.


An Assistive Technology Primer

Many users cannot use standard input or output devices, so one or more assistive technologies must be installed in their computing environments to enable their use. There are many types of assistive technologies, some of which affect application design, and some of which do not. For instance, sticky keys make it possible for a person with mobility impairments to type by pressing the Shift key (which "sticks" on) and subsequently pressing the letter to be capitalized with the same finger. This type of technology does not significantly affect application design and is handled by the computing platform.

On the other hand, many technologies need help from each application in order to perform their functions properly. When the input focus is on a component that requires input, sighted users can look at the screen near the text field and understand what information is appropriate. Non-sighted users rely on screen readers to explain the context to them, and the screen readers depend upon the applications to provide necessary semantic information. For instance, say that a blind user has used the tab key to move to an address entry field. If the application provides no accessibility information, the screen reader may only say "Editable Text," which does not provide enough information for the user to utilize the application. When the developer takes advantage of the component properties supported by the Java platform, the computer can say something more meaningful, such as "Address, Line 1" (more on these properties in Nuts and Bolts). Clearly, this additional information is indispensable to the user.

For other users, the operating system itself provides features that allow them to make effective use of well-behaved applications. For instance, manipulating an application's font can make the difference between a usable and an unusable application for a person with a visual disability. The issue of size is obvious: a person with limited vision benefits greatly from a font that is larger than the default. What may not be so obvious is the effect of the actual typeface. For instance, a block font may be much easier to read than a serif font, and custom fonts could be developed that aid an even broader population.

Similarly, the ability to customize colors is a necessity for some people. In the most common case, a selection of colors with high contrast is a great benefit. A less-obvious problem is that some users can see only certain colors and may require what seem strange color combinations to make an application usable.


Nuts and Bolts

The Java Accessibility API (JAAPI) is a standard extension in all releases of the Java 2 platform, and this means that the javax.accessibility package exists on any machine where Java 2 is installed. A component can utilize this extension simply by implementing the Accessible interface, which consists of exactly one method call, getAccessibleContext(). This call returns an instance of AccessibleContext that is specific to the component and provides the information and functionality necessary to enable its accessibility.

An AccessibleContext object provides a great deal of information regarding a component. Among other things, the context describes the component's role (e.g., a button or checkbox or one of many other roles), its accessible name (more on this soon), how many children it has, and a good deal more information. In addition, the context can be used to generate accessibility events when a noteworthy event has occurred. The standard documentation regarding this class is strongly recommended reading.

The good news is that all of the standard JFC/Swing! components implement the Accessible interface, and this means that most of the work has been done for applications developers. What follows are seven relatively simple guidelines that will finish the job.

1. Provide Accessible Names and Descriptions

The accessible name of an object is often the key to making that object usable by a person with disabilities. The name is a succinct explanation of the object's purpose, and an assistive technology will often present (e.g., speak) the name of each object encountered by a user. A user navigating a graphical interface will encounter many components, so the name of each component is preferably no more than one or two words, in order to avoid information overload. For instance, the fields of an address entry form could be named "Street," "City," and "Zip."

The accessible description of an object is a more verbose explanation and should be provided in cases where additional information may be useful. Normally, an assistive technology will retrieve the description when the user makes a request for more information about an object. Once the description has been retrieved, the technology can present the information to the user in a fashion that works well for him or her. Using the address example again, let's imagine a user with poor eyesight generating a billing statement. Via a screen magnifier, the user can see that s/he is entering a Zip Code, but does not know if the current field is part of the sender's or recipient's address. The user will request more information (perhaps by pressing a hotkey), and the assistive technology will read the description. If the developer set the accessible description, the computer could speak "Recipient's Zip Code."

When using the standard JFC/Swing! components, objects containing non-editable text will have their accessible names set automatically. Examples of these types of objects are menu items, buttons, radio boxes, and items in a list. However, some objects contain no static text and must have their names set by the developer. If the object on the screen has a label associated with it, the programmer's job is easy. Simply label the object with an instance of JLabel and use setLabelFor() to set the correct properties in the AccessibleContexts. This association is important because changes to the label automatically propagate to the assistive technology:

JTextField text = new JTextField(20);
JLabel label = new Label("Address Line 1");
label.setLabelFor(text);
// ... Add the text and label to a Container

Other objects do not have labels and require that the programmer set the name explicitly. For ImageIcon objects, a constructor exists that can be used to set the name:

ImageIcon(URL url, String name);

Accessible descriptions are set automatically by setting a tooltip on an object, via JComponent.setTooltipText(). This has the obvious advantage of not only aiding those with and without disabilities, but also ensures that the accessible description will be updated automatically with the tooltip.

As indicated by the above examples, the great majority of the JFC/Swing! components in an application can have their names and descriptions set automatically. This is by far the preferred method for setting these names, since any changes to the mainstream text via ongoing development or regional localization will automatically be reflected by all assistive technologies. In cases where a component has no label and no tooltip, the accessible strings can be placed directly in the object's AccessibleContext. It is important to realize that setting the values directly in this manner will permanently override the values pulled from the label or tooltip on the component:

AccessibleContext context = component.getAccessibleContext();
context.setAccessibleName("Zip");
context.setAccessibleDescription("Recipient's Zip Code");

It is reasonable to assume that an assistive technology will look for a description and finding none will fall back to using the accessible name of the object. For this and many other reasons, all objects must have accessible names.

2. Do Not Customize Fonts or Colors Unnecessarily

JFC/Swing! applications automatically inherit their font and color properties from the desktop and user preferences. In most cases, interface and application designers can achieve excellent visual results by simply accepting the user's preferences.

3. Implement Necessary Custom Colors and Fonts Via Properties

Accepting the user's preferences is best when possible, but some applications benefit from using specialized colors or fonts for specific objects. In these cases, the values should be stored in a properties file, which can be shipped with the application's class files. For instance, it would be sensible for a flight simulator to have a red "stop" button in Western regions, so we could define an appropriate property and store it in a file called flightsim.properties:

flightsim.stop.color=#ff0000

The program can then load the properties file as a ResourceBundle:

ResourceBundle resources = null;
Color stopColor = Color.red;
try {
resources = ResourceBundle.getBundle("flightsim");
String colorString = resources.getString("flightsim.stop.color");
stopColor = Color.decode(colorString);
} catch (MissingResourceException missingException) {
// Report the error, according to severity
}
// stopColor has now been customized

For most users, the existence of a properties file is transparent, but it provides the important ability to override the default values. In order to customize the properties, the user need only edit the properties file.

4. Use Dynamic GUI Layout

One of the most common mistakes made by developers using the Java platform is calling the setSize() method with constant values. Using constant parameters defeats dynamic layout and the resulting application will not adapt properly to users' settings. The full benefits of dynamic layout can be realized easily, by adjusting the size of each JFrame, JDialog, and JWindow at creation and each time its contents change:

// Invoke on each JFrame, JDialog, and JWindow at creation
// and whenever its contents change
window.setSize(window.getPreferredSize());

This allows all nested layout managers to affect the size and position of each object at runtime. In those rare instances where an object's preferred size is not acceptable, setSize() must be called with a dynamic value that ranges between the return values of getMinimumSize() and getMaximumSize().

A dynamic LayoutManager positions graphical objects relative to each other, so that changes in size are handled automatically and objects never obscure one another. Clearly, this repositioning is necessary so that font changes can be reflected in the user interface without hiding components and making them unusable. All of the JFC/Swing! layout managers are dynamic (e.g., BorderLayout, FlowLayout, and GridBagLayout), so an application normally needs to do nothing to get high-quality dynamic layout. That said, an application should never defeat the LayoutManager:

setLayout(null); // NEVER do this!

Defeating the LayoutManager makes it necessary for the developer to manually set the (x,y) position of every Component. Not only is this far more work but also yields an interface that will not properly adapt to many accessibility options, nor will it work properly with internationalization or user-defined preferences.

Implementing some user interfaces may require custom layout managers, and this should be done by implementing the LayoutManager2 interface (rather than the obsolete LayoutManager interface).When this is necessary, the layout manager should never set the positions of its managed components by using constant (x,y) values. Instead, the objects should be positioned relative to each other when the toolkit invokes layoutContainer() on the custom layout manager.

5. All Interface Components Must Be Keyboard Traversable

Many people cannot use a pointing device effectively, so this is far more important than it may appear at first glance. Pressing the tab key should move the input focus from component to component, and shift-tab should move the focus in the opposite direction. The default FocusManager used by the JFC/Swing! toolkit sets the focus order based on screen position, with focus moving from left-to-right and top-to-bottom. There are several ways to affect focus management, listed here from the simplest to the most complex:

  • When a component should not receive the input focus, create a subclass and override isFocusTraversable() to return false.

  • Invoke setNextFocusableComponent() on each JComponent to "hard-wire" the focus traversal order. Note that it is problematic to do this on some components and have others use the default ordering. If this method is used, it should be invoked on all JComponents in a window.

  • Implement a subclass of java.awt.FocusManager and install it with the static method FocusManager.setCurrentManager().

6. Use Mnemonics and Accelerators

Mnemonics are underlined characters that appear in menu items and on the buttons in some dialog boxes. A mnemonic can only be activated when the item is visible and does not require a modifier key (e.g., the user does not need to press the Alt key). All menu items must have mnemonics, so that keyboard-only use is practical.

Accelerators are displayed on menu items or buttons in parentheses after the item's text [e.g., "Save (Ctrl+S)"]. The accelerator requires the use of a modifier key, and can be activated any time the application's window has the input focus. Commonly used functionality should always be available via an accelerator. Using accelerators greatly improves the productivity of both power users and persons with mobility impairments.

7. Custom Components Must Implement Accessible

An application can work with an assistive technology only if all its GUI components implement the Accessible interface. All standard JComponent subclasses implement this interface and do everything necessary to be accessible, so this implies a corollary to this guideline: All custom components should extend a standard class as far down the JFC/Swing! inheritance hierarchy as possible. Of course, this is just plain good programming practice, since support for accessibility is just one of many behaviors that are inherited.

As an example, say that an application requires round buttons. If possible, the new button class should extend JButton, override all paint methods, and add support for any new properties. If the interface for JButton is too restrictive to allow for round buttons, then an attempt should be made to extend JButton's superclass, AbstractButton. Only if this class is found too restrictive should the new class extend directly from JComponent.

It makes sense for some custom components to derived directly from JComponent. It is important to be aware that JComponent does not implement Accessible, and the subclass must do more work in order to interoperate with assistive technologies. The good news is that JComponent actually has a good deal of latent support for accessibility, and once the subclass implements Accessible, it inherits that support. For instance, methods such as getAccessibleParent() and getAccessibleName() will work properly for most JComponent subclasses, with no additional code (other than 'implements Accessible').

Custom components will sometimes need to extend the accessibility behavior of their superclasses. For example, any component that derives directly from JComponent must at least express its accessible role as specifically as possible. These roles are a fairly large enumerated set defined in the AccessibleContext, with some examples being CANVAS, LABEL, and MENU.

Each JFC/Swing! class contains a protected inner class that actually does the accessibility work, and the root class is JComponent.AccessibleJComponent (in reality, this class derives from an inner class of java.awt.Component, but that is beyond the scope of this discussion). When a subclass needs to extend its accessibility behavior, a protected inner class should be created that extends its superclass' inner class. In addition, the component must override getAccessibleContext(), in order to create an instance of this new accessibility class. For instance, if a programmer creates a new class WarningLight that extends JComponent, the additional code would look something like this:

public class WarningLight extends JComponent implements Accessible {

public AccessibleContext getAccessibleContext() {
// variable accessibleContext is protected in superclass
if (accessibleContext == null) {
accessibleContext = new AccessibleWarningLight();
}
return accessibleContext;
}

protected class AccessibleWarningLight extends AccessibleJComponent {
public AccessibleRole getAccessibleRole() {
return AccessibleRole.ALERT;
}
}
// Implementation of WarningLight omitted...

This is all the code necessary to inherit the accessibility support available in JComponent and specify this object's role as an ALERT. It is very important to understand that the inner class is actually the AccessibleContext used by assistive technologies.


Test Cases

Once you've followed these guidelines, you'll want to test your application to determine how accessible it is to persons with disabilities. There is no substitute for good Human Interface design, but passing these tests will make the difference between a frustrating experience and productivity for many people.

1. Don't touch your mouse

Bring up each window and popup in your application and attempt to visit every component using only the Tab key on the keyboard. Once you are successful in that regard, you should actually use your application without touching the mouse, verifying that the application's features are all available. This test is important because some users simply cannot use a pointing device. For instance, a blind person can find the mouse and physically move it but has no idea where the pointer is positioned on the screen.

While doing this, you should also verify that:

  • Frequently-used functionality is directly accessible via an accelerator
  • All menu items have mnemonics
2. Change the default font and color

Choose a font of 24 points or larger, and colors other than the default. Bring up each window of the application and verify that screen objects do not overlap and that the colors are correct. If overlapping does occur, you will need to check the code that interacts with the LayoutManager in that window.

A relatively simple way to change the visual properties of a JFC/Swing! application is to create a new user interface theme for the metal look and feel. A sample look and feel has been included as Appendix A and includes a low-vision theme that uses large fonts and high-contrast colors. After compiling this file (LowVisionMetalLookAndFeel.java), add this code to your application so that it will be run before any UI elements have been created:

import LowVisionLookAndFeel;
// ... code omitted
try {
UIManager.setLookAndFeel("LowVisionMetalLookAndFeel");
} catch (Exception ex) {
System.out.println("Failed loading Low Vision Metal Look and Feel");
ex.printStackTrace(System.out);
}

This provides an excellent test of the application's response to user preferences.

3. Use a screen reader

Download and install a trial version of a screen reader that works with Java applications, such as IBM's Self-Voicing Kit software. Bring up each window in your application and tab to every component, verifying that you hear a reasonable description of each component as it receives the input focus. For instance, the label of a text field should be read when the text field receives the focus, and icons should cause their names to be read. If some Components do not announce themselves properly, you need to set their accessible names.

To get an even better idea of the accessibility of the application, turn your display off. Once you do that, there can be no cheating. Actually use your application in this mode, accessing both core functionality and more lightly-used features.


Bon Voyage!

Hopefully, you have an understanding of what it means for an application to be accessible. Utilizing that understanding and following these guidelines, you have the ability to leverage the JFC and provide a positive impact in the lives of many people with disabilities. The good news is that the JFC provides a great deal of this functionality automatically, and only a few of the principles described here are specific to accessible applications. These principles provide for better-behaved applications that make everyone more productive.


Appendix A: LowVisionMetalLookAndFeel

This look and feel and its associated theme do not include any standard icons, such as those that would be seen in error or warning dialogs. Normally, this is not a real problem and generates minor warning messages to the console. If the user chooses, s/he can copy the standard icons from the metal look and feel to avoid the warnings.

/*
 * LowVisionMetalLookAndFeel.java
 */
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import javax.swing.plaf.metal.*;

/**
 * Implements a Metal look and feel that automatically loads a low-vision
 * theme. The low-vision theme has large fonts and high-contrast colors
 *
 * @author Steve Wilson
 * @author Michael C. Albers
 * @author Jeff Dunn
 */
public class LowVisionMetalLookAndFeel extends MetalLookAndFeel {

  public LowVisionMetalLookAndFeel() {
  super();
  MetalTheme theme = new LowVisionMetalTheme();
  setCurrentTheme(theme);
  }

  protected class LowVisionMetalTheme extends DefaultMetalTheme {

    public String getName() { return "Low Vision"; }

    // Colors
    private final ColorUIResource primary1 = new ColorUIResource(0, 0, 0);
    private final ColorUIResource primary2 = new ColorUIResource(204, 204, 204);
    private final ColorUIResource primary3 = new ColorUIResource(255, 255, 255);
    private final ColorUIResource primaryHighlight =
    new ColorUIResource(102,102,102);
    
    private final ColorUIResource secondary2 =
    new ColorUIResource(204, 204, 204);
    private final ColorUIResource secondary3 =
    new ColorUIResource(255, 255, 255);
    private final ColorUIResource controlHighlight =
    new ColorUIResource(102,102,102);
    
    protected ColorUIResource getPrimary1() { return primary1; } 
    protected ColorUIResource getPrimary2() { return primary2; }
    protected ColorUIResource getPrimary3() { return primary3; }
    public ColorUIResource getPrimaryControlHighlight()
    { return primaryHighlight;}
    
    protected ColorUIResource getSecondary2() { return secondary2; }
    protected ColorUIResource getSecondary3() { return secondary3; }
    public ColorUIResource getControlHighlight()
    { return super.getSecondary3(); }
    
    public ColorUIResource getFocusColor() { return getBlack(); }
    
    public ColorUIResource getTextHighlightColor() { return getBlack(); }
    public ColorUIResource getHighlightedTextColor() { return getWhite(); }
      
    public ColorUIResource getMenuSelectedBackground() { return getBlack(); }
    public ColorUIResource getMenuSelectedForeground() { return getWhite(); }
    public ColorUIResource getAcceleratorForeground() { return getBlack(); }
    public ColorUIResource getAcceleratorSelectedForeground()
    { return getWhite(); }

    // Fonts
    private final FontUIResource controlFont = 
    new FontUIResource("Dialog", Font.BOLD, 24);
    private final FontUIResource systemFont =
    new FontUIResource("Dialog", Font.PLAIN, 24);
    private final FontUIResource windowTitleFont =
    new FontUIResource("Dialog", Font.BOLD, 24);
    private final FontUIResource userFont =
    new FontUIResource("SansSerif", Font.PLAIN, 24);
    private final FontUIResource smallFont =
    new FontUIResource("Dialog", Font.PLAIN, 20);
    
    public FontUIResource getControlTextFont() { return controlFont;}
    public FontUIResource getSystemTextFont() { return systemFont;}
    public FontUIResource getUserTextFont() { return userFont;}
    public FontUIResource getMenuTextFont() { return controlFont;}
    public FontUIResource getWindowTitleFont() { return windowTitleFont;}
    public FontUIResource getSubTextFont() { return smallFont;}
    
    public void addCustomEntriesToTable(UIDefaults table) {
      super.addCustomEntriesToTable(table);
    
      final int internalFrameIconSize = 30;
      table.put("InternalFrame.closeIcon",
      MetalIconFactory.getInternalFrameCloseIcon(internalFrameIconSize));
      table.put("InternalFrame.maximizeIcon",
      MetalIconFactory.getInternalFrameMaximizeIcon(internalFrameIconSize));
      table.put("InternalFrame.iconifyIcon",
      MetalIconFactory.getInternalFrameMinimizeIcon(internalFrameIconSize));
      table.put("InternalFrame.minimizeIcon",
      MetalIconFactory.getInternalFrameAltMaximizeIcon(internalFrameIconSize));
        
      Border blackLineBorder = new BorderUIResource(new LineBorder(getBlack()));
      Border whiteLineBorder = new BorderUIResource(new LineBorder(getWhite()));
      Border textBorder = blackLineBorder;
    
      table.put( "ToolTip.border", blackLineBorder);
      table.put( "TitledBorder.border", blackLineBorder);
      table.put( "Table.focusCellHighlightBorder", whiteLineBorder);
      table.put( "Table.focusCellForeground", getWhite());
      table.put( "TextField.border", textBorder);
      table.put( "PasswordField.border", textBorder);
      table.put( "TextArea.border", textBorder);
      table.put( "TextPane.border", textBorder);
      table.put( "ScrollPane.border", blackLineBorder);
      table.put( "ScrollBar.width", new Integer(25) );
    } // addCustomEntriesToTable()
  } // class LowVisionMetalTheme
} // class LowVisionMetalLookAndFeel()
Contact About Sun News & Events Employment Site Map Privacy Terms of Use Trademarks Copyright 1994-2008 Sun Microsystems, Inc.