所有 Java 应用程序对于残疾人士都应该是可访问的。用 GUI 应用程序实现这一点时需要格外注意。本文向您展示了如何使用基于 JFC/Swing 的可访问性工具箱事半功倍地实现最高级别的可访问性。
大多数基于 GUI 的软件设计都基于这一假设:即用户可以清楚地看到屏幕并且可以有效地使用鼠标选择图形用户界面(GUI)上的选项。对于许多残疾人士,尤其是那些视力和运动控制受损的人,这个假设是有问题的。1998 年,美国康复法案(Rehabilitation Act)经过修改
以确保残疾人可以访问政府使用的电子和信息技术产品。因此,许多 IT 企业已经开始采用可访问性指导原则作为其整个 GUI 设计标准的一部分。自从美国康复法案修正案通过以后,可访问性也成为商业软件设计中日益重要的问题,从而导致了 Java 平台的一些更改和添加。
本文有助于您快速了解一些联邦政府的可访问性要求,并且也有助于您使用 JFC/Swing 构建满足那些要求的 GUI。我开发了两个基于 Swing 的工具箱来辅助实现可访问性功能;这两个工具箱如下:
com.ibm.wac.AccessibilityUtils:一组可重用于任何 Swing GUI 的通用实用程序
com.ibm.wac.demos.AccessibilityDemo1:演示应用程序,它包括一组更特定于应用程序的实用程序,这些实用程序可重用于特定 GUI 中的类似结构
尽管您将在 AccessibilityDemo1 中看到的许多方法都是为单个应用程序创建的,但很容易使它们一般化以支持多个 GUI。
因为 JFC/Swing 是本文中讨论的所有 GUI 开发的基础,所以假定您熟悉使用 Swing 编程的基本概念。并进一步假定您正在使用 Java 版本 1.3.1 或更新的版本,因为我们将在这里讨论的部分方法在 Java 平台的较早版本中不可用。
重新审视您的 GUI
因为大多数 GUI 面向视力正常的人,所以对于弱视者或盲人,它们通常是作用有限的或无用的。同样,大多数 GUI 设计依赖鼠标进行导航,对于运动和视力有残疾的人,这会成为障碍。在本文中,我们将研究一些将可访问性功能添加到简单 GUI 的方法,侧重于那些针对视力和运动有残疾人士的功能。
“IBM Guidelines for Writing Accessible Applications Using 100% Pure Java”(请参阅参考资料)描述了可使残疾人士访问 Java 应用程序的应用程序设计和编码指南。在这些指南中,我们将侧重于下列内容:
为所有操作提供等价的键盘操作
在组件上设置助记符
为常用功能使用加速键
提供逻辑键盘跳格顺序
提供逻辑组件布局(用于多媒体访问)
标记组件
命名逻辑组
提供语义关系
描述图标与图形
请参阅参考资料以查看完整的指导原则文档。
盲人用户界面
盲人必须能够以不依赖于视觉反馈的方式访问应用程序的功能。针对盲人的最常用的辅助技术是“文本到语音”屏幕阅读器、可刷新的布莱叶盲文显示系统或相关的 I/O 设备。要使您的 Java 应用程序可访问,您需要以标准代码格式描述每个应用程序组件,这种格式可由辅助技术(AT)设备转换。例如,对于 GUI 组件(如按钮),您需要命名和描述其功能,然后将这些组件作为诸如“发送”、“进入”或“退出”之类的消息传递给用户。
一些 GUI 应用程序组件传达信息时比其它组件需要考虑更多的东西。例如,盲人如何才能访问表中可视化格式的信息,或图标、树或者滚动列表呢?要使这类组件可访问,要求您提供大量的文本形式的描述性信息。尽管这个任务可能很乏味,但它是迈向创建支持可访问性应用程序必不可少的步骤。
添加描述性信息
使您的应用程序对视力受损者可访问的第一步,是提供对将要接收焦点的组件的描述。当用户或 AT 阅读器选择组件时(通常是通过键盘控制装置),则组件接收焦点。接收焦点的组件对应用程序功能(而非其设计或布局)是不可或缺的。因此,打个比方说,包含其它组件的 JPanel 本身不接收焦点,尽管其内部的个别组件可能将接收焦点。另一方面,如果面板对信息进行了分组,有时则需要使该分组可访问。类似地,通过使用 setLabelFor(Component) 方法可将标签与其它组件相关联。
在 Swing 中,我们使用 javax.accessibility.Accessible 接口来提供关于应用程序组件的描述性信息。所有 Swing 组件都实现 Accessible 接口,该接口只有一个方法 javax.accessibility.AccessibleContext getAccessibleContext()。使用 getAccessibleContext() 方法,AT 阅读器可以访问所有它需要的信息来将组件的描述呈现给用户,并且与该组件交互和使用该组件。
AccessibleContext() 定义了两个可用来提供可访问信息的方法:
setAccessibleName(String name) 设置与给定 Accessible 对象相关联的 name。通常,只要组件接收到焦点,辅助阅读器就会提供这个名称。
setAccessibleDescription(String description) 设置与给定 Accessible 对象相关联的 description。通常,当用户要求关于组件的更多详细信息时,辅助阅读器将提供这个描述。
标准 Swing 组件通常都为 AccessibleName 和 AccessibleDescription 提供了缺省值。例如,JLabel 或 JTextField 文本将被用作其缺省的可访问名称。同样,任何组件的 ToolTip 将被用作其缺省的可访问描述。但是,我的经验表明,缺省值不能为给定组件提供最佳名称或描述,因此我建议您显式地设置您的组件值。
要设置文本域值,您需要输入一些类似于清单 1 所示的代码:
清单 1. 设置文本域值
import javax.swing.*;
:
JTextField streetField = new JTextField("", 20);
streetField.setName("streetField");
streetField.getAccessibleContext().
setAccessibleName("Street Entry Field");
streetField.getAccessibleContext().
setAccessibleDescription("Enter a street address");
streetField.setToolTip("Street Address");
:
-- set any other desired characteristics --
类似地,要设置按钮的值,您可以输入清单 2 中所示的代码:
清单 2. 设置按钮的值
import javax.swing.*;
:
JButton okButton = new JButton("OK");
okButton.setName("okButton");
okButton.getAccessibleContext().setAccessibleName("OK Button");
okButton.getAccessibleContext().setAccessibleDescription(
"Activate to commit changes and continue");
okButton.setToolTip("Commit changes");
okButton.setMnemonic((int)'O');
:
-- set any other desired characteristics --
可访问键盘导航
通常 Swing 允许用跳格(Tab)、反向跳格和箭头键进行键盘导航。遗憾的是,这个系统难以实现并且很费时,因为它要求用户导航所有中间组件才能到达它需要的那个组件。对于更有效的键盘导航,用户应该能够迅速地在重要组件之间切换,而无须考虑它们在 GUI 布局中的顺序。我们可以将助记符键盘设置用于 javax.swing.AbstractButton 和 javax.swing.JLabel 的子类以及应用程序菜单中的项。助记符通常称为加速键,因为它们根据 GUI 内容直接进行工作。
在为您的界面建立了助记符系统之后,用户就可以通过使用 Alt 键和键盘上表示该组件的助记符键(Alt+键)导航到任何想用的组件上。但是这种设置有一个问题,就是它对于顶级组件(通常是 JFrame 或 JDialog)而言是全局的。这意味着基本上只有 26 个唯一值,却要分配给所有菜单和菜单项以及基本的 GUI 内容。在频繁使用的 GUI 上并非所有组件都可以链接到助记符键,因此您必须确定哪些组件对于用户是最重要的,然后相应的设置它们。我建议您为菜单项、重要的操作按钮(如 OK 或 Cancel)和 GUI 中每个逻辑组中的初始组件创建助记符链接,然后让用户跳格到其它每个组件上。
设置跳格顺序和初始焦点
对于大多数基于跳格的逻辑导航,我建议您将组件按您希望跳格选择的顺序添加到容器中。您可能希望以相同的方式组织嵌套的容器(即 JPanel)。尽管从上到下、从左向右(T2B、L2R)的顺序是标准的,但您可能希望建立不同的系统,如基于列排列的系统。您可以使用方法 JComponent.setNextFocusableComponent(Component c)(或 Java 1.4 中的类 java.awt.FocusTraversalPolicy)来强制规定定制的跳格顺序。AccessibilityDemo1 GUI 说明了一个跳格系统,该系统基于将组件以 T2B、L2R 顺序添加到容器中。
在定义了跳格顺序之后,您需要确保每个初始组件都在选中其容器时接收到焦点。当容器接收到焦点时(请参阅参考资料以获取关于 FocusListener 的更多信息),它应该向期望的初始组件发出 java.awt.Component.requestFocus()(在 Java 1.4 中是 java.awt.Component.requestFocusInWindow())。
另一种方法是在窗口激活时设置初始焦点。例如,下列代码将 WindowListener 添加到 JFrame,后者在窗口被激活时为 JTextField 请求焦点。
清单 3. 在激活时设置初始焦点
import java.awt.event.*;
import javax.swing.*;
:
JFrame frame = new JFrame();
JTextField field = new JTextField();
/// field to get initial focus
boolean focusSet;
:
frame.addWindowListener(new WindowAdapter() {
public void windowActivate() {
if ( !focu