Chapter 24. Accessibility

In this chapter:

24.1 Java 2 Accessibility API overview

When developing an application, it is important to keep in mind that many of it's users may be disabled to some degree. Disabilities can range anywhere from poor vision to blindness, hearing impared to deafness, or a physical inability to use a standard input device such as a mouse or keyboard. Many of these users may often rely on special devices plugged into the computer to help offset specific disabilities. Some of these devices can substitute printed text with audio output, ease keyboard input or point-and-click functionality, perform large scale screen magnification, etc. To do their job correctly, most of these devices need to find connection points within applications, and substitute some of the original functionality with accessible variations.

Java offers sets of classes and interfaces to provide the needed connection points for such special devices. These are referred to as assistive technologies, and are normally packages in a JAR archive. Java loads assistive technologies when the VM is started by looking for special entries in a file called accessibility.properties located in the jdk1.2\jre\lib directory. We will discuss how to use this file below.

24.1.1 Implementing accessibile applications

Fortunately, Swing provides an extremely rich API for accessibility needs. If we use Swing "as is", there are only a few simple things we need to do in order to provide an accessible interface for assistive technologies:

Use tooltips to provide a text description of a component's purpose. The tooltip text may be used by accessible devices to provide a description of that component.

Enable keyboard access. Ideally, all functionality should be available without using the mouse. Use mnemonics for buttons and menus, set keyboard accelerators in menus.

Associate labels with components. By setting both the displayedMnemonic and labelFor properties, a label will transfer focus to the specified component with the labelFor property when the displayedMnemonic is activated (i.e. when ALT + mnemonic character is pressed).

Logically group components with labels refering to each when necessary.

Throughout this book we have adhered to most of these rules without explicitly being aware that we were also building accessible-compliant applications. Let's review a bit more about the association of labels with components. Consider the following code:

    JPanel p = new JPanel();
    p.add(new JLabel("IP address:"));
    JTextField user = new JTextField();
    p.add(user);

To make this code accessible we would need to associate the label with the text field above as follows:

    JPanel p = new JPanel();
    JLabel lbl = new JLabel("IP address:");
    lbl.setDisplayedMnemonic('n');
    p.add(lbl);
    JTextField user = new JTextField();
    user.setToolTipText("IP address");
    lbl.setLabelFor(user);
    p.add(user);

The changes made should be familiar and are relatively simple. First, the setDisplayedMnemonic() method assigns a mnemonic character to the label component (this character will be underlined in the label if the label’s text contains that character). Second, a tooltip text is assigned to the text field to provide an accessible text description. Finally, the setLabelFor() method is called to associate the text field component with the label. When the mnemonic is activated, the label will notify the text field to request the focus.

 

Note: As we've mentioned in chapter 19, due to a Swing bug or oversite, a mnemonic character will be passed to the text field as input. We will not concentrate on a workaround in this chapter, assuming that it will be fixed in future Swing releases.

 

Reference: To find out more about writing accessible Java applications, we refer you to IBM’s guidelines at http://www.austin.ibm.com/sns/access.html.

Java 2 ships with the javax.accessibility package, which includes classes and interfaces constituting the Java Accessibility API. In the remainder of this section we'll give a brief introduction to some of its most important constituents.

 

Note: You may not need to explicitly use this package in your Swing application. Almost all of Swing has accessibile functionaliy built into it.

24.1.3 The Accessible interface

abstract interface javax.accessibility.Accessible

This interface must be implemented by all Java classes designed to support accessibility. All Swing components implement this interface either directly or indirectly. It declares only one method, getAccessibleContext(), which returns an instance of AccessibleContext (see below).

24.1.4 AccessibleContext

abstract class javax.accessibility.AccessibleContext

This class provides the minimum information all accessible objects should expose. This information includes the accessible: name, description, role, and state of the object, as well as information about the parent and children of the object. To obtain more specific accessibility information about a component this class exposes methods to retrieve the instances of interfaces defined in the accessibility package (see below).

Typically Swing components define an inner class which extends AccessibleContext and provides information specific to that component. For instance, JComponent provides the inner class AccessibleJComponent, JComboBox provides AccessibleJComboBox etc.

24.1.5 AccessibleRole

class javax.accessibility.AccessibleRole

This class defines several static constants used in providing information about the role a particular component plays in an application. Normally the getAccessibleRole() method of AccessibleContext will return one of these constants.

24.1.6 The AccessibleAction interface

abstract interface javax.accessibility.AccessibleAction

This interface declares methods to perform one or more accessible actions, retrieve descriptions of these actions, and find out exactly how many actions have been exposed to assistive technologies.

int getAccessibleActionCount(): returns the number of actions exposed through the doAccessibleAction() method.

String getAccessibleActionDescription(int i): should be defined to return a description of the code executed by doAccessibleAction() based on the given index.

boolean doAccessibleAction(int i): When implementing this interface we are expected to define code to be executed based on a given index. Normally this is involves invoking event handling code defined for a specific component, however, there is nothing stopping us from defining accessible-specific code here. It should return a boolean value representing whether or not the given index corresponds to a valid action.

24.1.7 The AccessibleComponent interface

abstract interface javax.accessibility.AccessibleComponent

This interface should be supported by any accessible component that is rendered to a graphical context. It provides the standard mechanism for an assistive technology to determine and assign certain aspects of the graphical representation of that component. Most methods will call the corresponding component’s methods where applicable. (Note that no painting methods are members of this interface because we are expected to define our own look-and-feel if a more customized view is desired.)

24.1.8 The AccessibleHypertext interface

abstract interface javax.accessibility.AccessibleHypertext

This interface provides standard mechanisms for determining and manipulating hyperlinks with an assistive technology. Normally only text components will provide implementations of this interface, however, any other component supporting hypertext may do the same.

24.1.9 The AccessibleSelection interface

abstract interface javax.accessibility.AccessibleSelection

This interface should be implemented by any accessible component that has selectable children, whether individually or in groups, such as trees, tables, lists, tabbed panes, menu bars, etc.

24.1.10 The AccessibleText interface

abstract interface javax.accessibility.AccessibleText

This interface should be implemented by any accessible text component, and is intended to provide access to that component’s document content and attributes (where applicable).

24.1.11 The AccessibleValue interface

abstract interface javax.accessibility.AccessibleValue

This interface should be implemented by any accessible component that supports a numerical value (e.g. a scroll bar). It is intended to be used by an assistive technology to determine and assign current, minimum, and maximum values.

24.1.12 Accessibility Utilities

In order to provide accessibility compliance between an application and an assistive technology, we cannot just rely on our use of the accessibility package. We also require support for locating the objects that implement accessibility functionality, as well as support for loading assistive technologies into the Java virtual machine and tracking all events sent to the system event queue. For these reasons, Sun provides the Java Accessibility Utilities which can be downloaded from the JavaSoft web site. Along with utility classes to perform these services, a set of sample applications is included which can be used to test and debug accessible Java programs. These applications are capable of linking with the system event queue and displaying information about selected types of events.

The Java Accessibility Utilities package is an assistive technology itself. To install it we must place the jaccess.jar and jaccess-examples.jar files into our jdk1.2\jre\lib\ext directory.

To use one of its assistive-technology examples, we need to include a string such as the following in our accessibility.properties file:

assistive_technologies=Explorer

This will cause the Explorer utility to start each time we start a new JVM. (Note that if this file does not already exits we must create it ourselves.) In the examples below we assume that you have enabled the Explorer utility, and it is shown in the corresponding example figures.

 

Reference: A detailed description of the Java Accessibility Utilities lies beyond the scope of this book. Please see http://java.sun.com/products/jfc/jaccess-1.2/doc/guide.html for more information.

 

Note: Accessibility Utilities can be used by any Java application to monitor the content of the system event queue for debugging purposes.

 

24.2 Following accessibility guidelines

Let's take a look at how the guidelines for writing an accessible application can be fully adhered to in one of our previous examples: the FTP Client application from chapter 13. As you will see, only minor changes need to be made for full compliance.

Figure 24.1 FTP Client application with added accessibility support, and monitored using the Explorer utility.

<<file figure24-1.gif>>

The Code: Access1.java

see \Chapter24\1

// Unchanged imports from section 13.5

public class Access1 extends JFrame 
{
  // Unchanged code from section 13.5

  public Access1()
  {
    super("FTP Client [Accessible]");
    getContentPane().setLayout(new BorderLayout());
    
    JPanel p = new JPanel();
    p.setLayout(new DialogLayout2(10, 5));
    p.setBorder(new EmptyBorder(5, 5, 5, 5));

    JLabel lbl = new JLabel("User name:");
    lbl.setDisplayedMnemonic('n');
    p.add(lbl);
    m_txtUser = new JTextField("anonymous");
    m_txtUser.setToolTipText("User name");
    p.add(m_txtUser);
    lbl.setLabelFor(m_txtUser);

    lbl = new JLabel("Password:");
    lbl.setDisplayedMnemonic('p');
    p.add(lbl);
    m_txtPassword = new JPasswordField();
    m_txtPassword.setToolTipText("Password");
    p.add(m_txtPassword);
    lbl.setLabelFor(m_txtPassword);

    lbl = new JLabel("URL:");
    lbl.setDisplayedMnemonic('u');
    p.add(lbl);
    m_txtURL = new JTextField();
    m_txtURL.setToolTipText("URL");
    p.add(m_txtURL);
    lbl.setLabelFor(m_txtURL);

    lbl = new JLabel("File:");
    lbl.setDisplayedMnemonic('l');
    p.add(lbl);
    m_txtFile = new JTextField();
    m_txtFile.setToolTipText("File");
    p.add(m_txtFile);
    lbl.setLabelFor(m_txtFile);

    // Unchanged code from section 13.5

    p.add(new DialogSeparator());
    m_btPut = new JButton("Put");
    m_btPut.setToolTipText("Upload file");
    // Unchanged code from section 13.5
    p.add(m_btPut);

    m_btGet = new JButton("Get");
    m_btGet.setToolTipText("Download file");
    // Unchanged code from section 13.5
    p.add(m_btGet);

    m_btFile = new JButton("File");
    m_btFile.setToolTipText("Select file");
    // Unchanged code from section 13.5
    p.add(m_btFile);

    m_btClose = new JButton("Close");
    m_btClose.setToolTipText("Quit application");
    // Unchanged code from section 13.5
    p.add(m_btClose);

    // Unchanged from section 13.5

Understanding the Code

Class Access1

Compared to the example in chapter 13 we have made some a very minor amount of changes, and those we did make are quite simple. All labels receive a displayedMnemonic property and are associated with the corresponding text fields using the setLabelFor() method. We’ve also added tooltip text for each text field and label.

Running the Code

At this point you can compile and execute this example. Figure 24.1 shows our FTP Client application and the Explorer accessible utility displaying events generating by that application. Unfortunately we cannot implement actual support for disabled users because no real assistive technologies are currently available.

24.3 Accessibility for custom components

In developing professional custom Swing components, special care should be taken to implement the Accessible interface and provide a custom extension of the AccessibleContext class. The following example extends the functionality of our OpenList component, developed and used in the examples of chapter 20, to comply with the accessibility standards.

Figure 24.2 Custom font dialog using accessible OpenList components, and monitored using the Explorer utility .

<<file figure24-2.gif>>

The Code: Access2.java

see \Chapter24\2

import javax.accessibility.*;

public class Access2 extends JDialog
{
  // added mnemonics and tooltip text to all relevant components
  // ...this class is used to set up a font dialog and has been
  // extracted from code in chapter 20. It requires little 
  // presentation or explanation here.

  public static void main(String argv[]) {
    GraphicsEnvironment ge = GraphicsEnvironment.
      getLocalGraphicsEnvironment();
    m_fontNames = ge.getAvailableFontFamilyNames();
    m_fontSizes = new String[] {"8", "9", "10", "11", "12", "14",
      "16", "18", "20", "22", "24", "26", "28", "36", "48", "72"};

    Access2 dlg = new Access2(new JFrame());
    SimpleAttributeSet a = new SimpleAttributeSet();
    StyleConstants.setFontFamily(a, "Monospaced");
    StyleConstants.setFontSize(a, 12);
    dlg.setAttributes(a);
    dlg.show();
  }
}

class OpenList extends JPanel implements ListSelectionListener, ActionListener
{
  protected JLabel m_title;
  protected JTextField m_text;
  protected JList m_list;
  protected JScrollPane m_scroll;

  public OpenList(String[] data, String title)
  {
    setLayout(null);
    m_title = new OpenListLabel(title, JLabel.LEFT);
    add(m_title);
    m_text = new OpenListText();
    m_text.addActionListener(this);
    m_title.setLabelFor(m_text);
    add(m_text);
    m_list = new OpenListList(data);
    m_list.setVisibleRowCount(4);
    m_list.addListSelectionListener(this);
    m_scroll = new JScrollPane(m_list);
    add(m_scroll);
  }

  public OpenList(String title, int numCols) {
    setLayout(null);
    m_title = new OpenListLabel(title, JLabel.LEFT);
    add(m_title);
    m_text = new OpenListText(numCols);
    m_text.addActionListener(this);
    m_title.setLabelFor(m_text);
    add(m_text);
    m_list = new OpenListList();
    m_list.setVisibleRowCount(4);
    m_list.addListSelectionListener(this);
    m_scroll = new JScrollPane(m_list);
    add(m_scroll);
  }

  public void setToolTipText(String text) {
    super.setToolTipText(text);
    m_title.setToolTipText(text);
    m_text.setToolTipText(text);
    m_list.setToolTipText(text);
  }

  public void setDisplayedMnemonic(char ch) {
    m_title.setDisplayedMnemonic(ch);
  }

  public AccessibleContext getAccessibleContext() {
    if (accessibleContext == null)
      accessibleContext = new AccessibleOpenList();
    return accessibleContext;
  }

  // Unchanged code from section 20.9

  class OpenListLabel extends JLabel {
    public OpenListLabel(String text, int alignment) {
      super(text, alignment);
    }

    public AccessibleContext getAccessibleContext() {
      return OpenList.this.getAccessibleContext();
    }
  }

  class OpenListText extends JTextField
  {
    public OpenListText() {}

    public OpenListText(int numCols) {
      super(numCols);
    }

    public AccessibleContext getAccessibleContext() {
      return OpenList.this.getAccessibleContext();
    }
  }

  class OpenListList extends JList
  {
    public OpenListList() {}

    public OpenListList(String[] data) { super(data); }

    public AccessibleContext getAccessibleContext() {
      return OpenList.this.getAccessibleContext();
    }
  }

  protected class AccessibleOpenList extends AccessibleJComponent 
  {
    public String getAccessibleName() {
      if (accessibleName != null)
        return accessibleName;
      return m_title.getText();
    }

    public AccessibleRole getAccessibleRole() {
      return AccessibleRole.LIST;
    }
  }
}

Understanding the Code

Class Access2

This class is mainly extracted from the Editor6 class of chapter 20 and represents an extension of our FontDialog component. We have simply added tooltip text and mnemonics to each of its children and have defined a main method to construct and display an instance of this component.

Class OpenList

Compared to the example in chapter 20 we haven’t changed much. We now sub-class three components to build inner class, accessible counterparts: OpenListLabel extends JLabel, OpenListText extends JTextField, and OpenListList extends JList (see below for more about these sub-classes). Also note that the label component is linked with the text component using the setLabelFor() method as discussed above.


The
setToolTipText() method is overridden to assign a given tooltip text to all three sub-components. The setDisplayedMnemonic() method is exposed assign the displayedMnemonic property of the label component. In conjunction with the setLabelFor() method call included in the constructors, this provides additional keyboard access to our OpenList component.

The getAccessibleContext() method is the only remaining change made to our OpenList class. It is required for implementation of the Accessible interface inherited from its JPanel ancestor. Our implementation fills the accessibleContext instance variable (inherited from JComponent) by creating an instance of our AccessibleOpenList inner class, and returns it as an AccessibleContext.

Class OpenList.OpenListLabel

This inner class extends JLabel to override the getAccessibleContext() method and delegates the call to OpenList‘s getAccessibleContext() method. The same is true for inner OpenListText and OpenListList sub-classes of JTextField and JList respectively. This guarantees that the AccessibleOpenList will be used as the accessible context, no matter which sub-component currently has the focus. In this way, information about OpenList and each of its constituent child components is always grouped together and treated as one component by assistive technologies.

Class OpenList.AccessibleOpenList

This inner class extends the AccessibleJComponent class (an extension of AccessibleContext provided by the JComponent ancestor) to provide accessible information about our component. The getAccessibleName() method returns the text of the m_title label as the accessible name (unless this has been set directly with the setAccessibleName() method). The getAccessibleRole() method returns Acc! essibleRole.LIST as the closest pre-defined accessible role (see 24.1.5).

 

Note: We could have gone much further in developing a custom accessible context. However, in the absense of real assistive technologies this does not make much sense. We hope to have provided a sufficient background to get you started when these are made available.

Running the Code

At this point you can compile and execute this example. Figure 24.2 shows the font dialog from our word processor application of chapter 20 now using an accessible variant of the OpenList component. Notice that when focus is transferred to an OpenList component an instance of AccessibleOpenList, described above, is used as the accessible context (the Explorer window in figure 24.2 illustrates).