如何支持辅助技术

您可能想知道什么是辅助技术,以及为什么要关心。首先,存在辅助技术以使永久或暂时性残障人士能够使用计算机。例如,如果您患有腕管综合症,则可以使用辅助技术来完成工作,而无需动手。

语音interface,屏幕阅读器,备用 Importing 设备等辅助技术不仅对残疾人有用,而且对在非办公室环境中使用计算机的人也很有用。例如,如果您陷入交通拥堵,则可以使用辅助技术来仅通过语音 Importing 和输出来检查电子邮件。支持辅助技术的信息也可以用于其他工具,例如自动 GUI 测试仪和 Importing 设备(例如触摸屏)。辅助技术使用javax.accessibility软件包中定义的 Accessibility API 从组件中获取信息。

由于 Swing 组件内置了对 Accessibility API 的支持,因此即使您没有做任何特别的事情,您的 Swing 程序也可能会与辅助技术一起正常工作。例如,辅助技术可以自动获取由以下代码行设置的文本信息:

JButton button = new JButton("I'm a Swing button!");
label = new JLabel(labelPrefix + "0    ");
label.setText(labelPrefix + numClicks);
JFrame frame = new JFrame("SwingApplication");

辅助技术还可以获取与组件关联的工具提示文本(如果有),并使用它向用户描述组件。

使用辅助技术使您的程序平稳运行很容易,而且在美国,联邦法律可能要求这样做。

本节的其余部分涵盖以下主题:

支持辅助功能的规则

您可以采取一些措施来使程序在辅助技术的帮助下尽可能地正常运行:

  • 如果组件不显示短字符串(用作其默认名称),请使用setAccessibleName方法指定名称。您可能希望对仅图像按钮,提供逻辑分组的面板,文本区域等进行此操作。

  • 尽可能为组件设置tool tip文本。例如:

aJComponent.setToolTipText(
     "Clicking this component causes XYZ to happen.");
  • 如果您不想为组件提供工具提示,请使用setAccessibleDescription方法来提供辅助技术可以为用户提供的描述。例如:
aJComponent.getAccessibleContext().
    setAccessibleDescription(
    "Clicking this component causes XYZ to happen.");
  • 尽可能指定替代键盘。确保您只能通过键盘使用程序。try隐藏鼠标!请注意,如果焦点在可编辑的文本组件中,则可以使用 Shift-Tab 将焦点移到下一个组件。

对键盘替代品的支持因组件而异。 Buttons支持setMnemonic方法的键盘替代品。菜单继承了按钮助记符支持,还支持加速器,如启用键盘操作中所述。其他组件可以使用key bindings将用户键入与程序操作相关联。

  • 为程序中的所有ImageIcon对象分配文本描述。您可以使用setDescription方法或ImageIcon构造函数的String形式之一来设置此属性。

  • 如果一堆组件组成一个逻辑组,请try将它们放入一个容器中。例如,使用JPanel将所有单选按钮包含在单选按钮组中。

  • 每当您拥有描述另一个组件的label时,请使用setLabelFor方法,以便辅助技术可以找到与标签关联的组件。当标签显示另一个组件(例如文本字段)的助记符时,这一点尤其重要。

  • 如果创建自定义组件,请确保它支持辅助功能。特别要注意,JComponent的子类不能自动访问。作为其他 Swing 组件的后代的自定义组件应在必要时覆盖继承的可访问性信息。有关更多信息,请参见概念:辅助功能的工作原理使自定义组件可访问

  • 使用可访问性 Util 随附的示例来测试您的程序。尽管这些示例的主要 Object 是向程序员展示在实现辅助技术时如何使用 Accessibility API,但是这些示例对于测试应用程序的可访问性也非常有用。 辅助功能测试显示ScrollDemo与 Monkey 一起运行,这是辅助功能 Util 示例之一。 Monkey 显示了程序中可访问组件的树,并允许您与这些组件进行交互。

  • 最后,不要破坏免费获得的东西!如果您的 GUI 拥有一个无法访问的容器(例如,您自己的ContainerJComponent的子类或任何其他未实现Accessibleinterface的容器),则该容器内的任何组件都将无法访问。

可访问性测试

可访问性 Util 附带的示例可以使您了解程序的可访问性。有关获取这些 Util 的说明,请参见Java SE Desktop 辅助功能主页。请遵循可访问性 Util 文档中的说明,以设置 Java 虚拟机(VM)以自动运行一个或多个 Util。

让我们使用一个可访问性 Util 将一个演示的原始版本与已应用支持可访问性规则的版本进行比较。这是一个名为ScrollDemo的程序的图片。

原始的 ScrollDemo 的快照。

Try this:

  • 单击启动按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行ScrollDemo。或者,要自己编译并运行示例,请参考example index

  • 接下来,单击“启动”按钮以使用Java™Web 开始(下载 JDK 7 或更高版本)运行AccessibleScrollDemo。或者,要自己编译并运行示例,请参考example index

  • 并排比较两个版本。唯一值得注意的区别是, cm 切换按钮和照片在可访问的版本中具有工具提示。

  • 现在,在名为 Monkey 的可访问性 Util 下运行两个版本。请注意,当已在accessibility.properties文件中下载并配置了辅助功能工具后,单击“运行 ScrollDemo”和“ AccessibleScrollDemo”链接(在步骤 1 和 2 中),将自动显示“猴子”窗口。

如果启动时没有显示 Monkey 窗口,则可能是 Java Web Start 使用的 VM 版本中不存在accessibility.properties文件。您可以通过运行 Java Web Start Application Manager并选择 File> Preferences> Java 来更改所使用的 VM。

  • 请注意,当猴子窗口出现时,您需要选择 文件>刷新树 ,以查看信息出现在Accessible Tree下。然后,您可以通过依次单击每个文件夹图标显示的水平图标来展开树。展开树后,您可以查看各个组件的详细信息。原始版本中无法访问的自定义组件(规则和角)可以在修改后的版本中访问。这可以对辅助技术产生很大的影响。

这是在ScrollDemo上运行的 Monkey 的快照:

猴子在无法访问的 ScrollDemo 版本上运行。

拆分窗格的左侧显示了程序的实际组件层次结构。右侧显示了层次结构中可访问的组件,这是我们感兴趣的。

首先要注意的是,即使在ScrollDemo中没有显式支持,Monkey 也能够发现有关程序中各个组件的大量信息。大多数组件及其子代出现在树中。但是,大多数组件的名称都是空的(null),这无济于事。说明也为空。

该程序的自定义组件带来了进一步的麻烦。这两个标尺不可访问,因此它们不包含在可访问树中。包含标尺的视口将显示为叶节点,因为它们没有可访问的子级。自定义角也从可访问树中丢失。

现在,这是AccessibleScrollDemo的 Monkey 窗口的图片:

猴子在可访问版本的 ScrollDemo 上运行。

现在,规则被列为视口的子级,而角被列为滚动窗格的子级。此外,许多组件现在都具有非空名称。

在 Monkey 的上一个快照中,选择了“ Column Header”项。 Monkey 突出显示ScrollDemo程序中的相应组件。

AccessibleScrollDemo 屏幕截图。

选择一个项目后,您可以使用 Monkey 的“面板”菜单调出四个不同面板之一,以使您可以与所选组件进行交互。选择“面板> Accessibility API 面板”,将弹出一个面板,如下图所示。该面板显示通过AccessibleContextBase Class 和AccessibleComponentinterface中定义的方法可用的信息。

列标题的辅助功能 API 面板。

猴子还有其他三个小组:

  • AccessibleAction :显示可访问组件支持的操作,并允许您调用该操作。仅与上下文实现AccessibleActioninterface的可访问组件一起使用。

  • AccessibleSelection :显示可访问组件的当前选择,并允许您操纵选择。仅与上下文实现AccessibleSelectioninterface的可访问组件一起使用。

  • AccessibleHypertext :显示可访问组件中包含的所有超链接,并允许您遍历它们。仅与上下文实现AccessibleHypertextinterface的可访问组件一起使用。

可访问性 Util 示例很容易用作测试工具,可以使您了解程序中组件的可访问性。但是,即使您的组件在 Monkey 或其他示例中表现良好,它们仍可能无法完全访问,因为 Monkey 和其他示例仅行使 Accessibility API 的某些部分。

唯一可访问性的 true 测试是使用现实世界的辅助技术运行程序,但是,您可能会发现以下免费的开源屏幕阅读器很有用:非可视桌面访问(NVDA)

在组件上设置可访问的名称和描述

为程序的组件提供可访问的名称和描述是使程序可访问的最简单也是最重要的步骤之一。以下是AccessibleScrollDemo构造函数的完整列表,该构造函数创建滚动窗格及其使用的自定义组件。粗体字声明提供了辅助技术可以使用的组件名称和描述。

public AccessibleScrollDemo() {
    // Get the image to use.
    ImageIcon bee = createImageIcon("images/flyingBee.jpg",
                      "Photograph of a flying bee.");

    // Create the row and column headers.
    columnView = new Rule(Rule.HORIZONTAL, true);
    if (bee != null) {
        columnView.setPreferredWidth(bee.getIconWidth());
    } else {
        columnView.setPreferredWidth(320);
    }
    columnView.getAccessibleContext().setAccessibleName("Column Header");
    columnView.getAccessibleContext().
            setAccessibleDescription("Displays horizontal ruler for " +
                                     "measuring scroll pane client.");
    rowView = new Rule(Rule.VERTICAL, true);
    if (bee != null) {
        rowView.setPreferredHeight(bee.getIconHeight());
    } else {
        rowView.setPreferredHeight(480);
    }
    rowView.getAccessibleContext().setAccessibleName("Row Header");
    rowView.getAccessibleContext().
            setAccessibleDescription("Displays vertical ruler for " +
                                     "measuring scroll pane client.");

    // Create the corners.
    JPanel buttonCorner = new JPanel();
    isMetric = new JToggleButton("cm", true);
    isMetric.setFont(new Font("SansSerif", Font.PLAIN, 11));
    isMetric.setMargin(new Insets(2,2,2,2));
    isMetric.addItemListener(this);
    isMetric.setToolTipText("Toggles rulers' unit of measure " +
                            "between inches and centimeters.");
    buttonCorner.add(isMetric); //Use the default FlowLayout
    buttonCorner.getAccessibleContext().
                 setAccessibleName("Upper Left Corner");

    String desc = "Fills the corner of a scroll pane " +
                  "with color for aesthetic reasons.";
    Corner lowerLeft = new Corner();
    lowerLeft.getAccessibleContext().
              setAccessibleName("Lower Left Corner");
    lowerLeft.getAccessibleContext().setAccessibleDescription(desc);

    Corner upperRight = new Corner();
    upperRight.getAccessibleContext().
               setAccessibleName("Upper Right Corner");
    upperRight.getAccessibleContext().setAccessibleDescription(desc);
    
    // Set up the scroll pane.
    picture = new ScrollablePicture(bee,
                                    columnView.getIncrement());
    picture.setToolTipText(bee.getDescription());
    picture.getAccessibleContext().setAccessibleName(
                                     "Scroll pane client");

    JScrollPane pictureScrollPane = new JScrollPane(picture);
    pictureScrollPane.setPreferredSize(new Dimension(300, 250));
    pictureScrollPane.setViewportBorder(
            BorderFactory.createLineBorder(Color.black));

    pictureScrollPane.setColumnHeaderView(columnView);
    pictureScrollPane.setRowHeaderView(rowView);

    // In theory, to support internationalization you would change
    // UPPER_LEFT_CORNER to UPPER_LEADING_CORNER,
    // LOWER_LEFT_CORNER to LOWER_LEADING_CORNER, and
    // UPPER_RIGHT_CORNER to UPPER_TRAILING_CORNER.  In practice,
    // bug #4467063 makes that impossible (at least in 1.4.0).
    pictureScrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER,
                                buttonCorner);
    pictureScrollPane.setCorner(JScrollPane.LOWER_LEFT_CORNER,
                                lowerLeft);
    pictureScrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER,
                                upperRight);

    // Put it in this panel.
    setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
    add(pictureScrollPane);
    setBorder(BorderFactory.createEmptyBorder(20,20,20,20));
}

通常,程序直接通过组件的可访问上下文来设置组件的名称和描述。有时,程序会通过工具提示间接设置可访问的描述。对于 cm 切换按钮,说明将自动设置为按钮上的 Literals。

概念:辅助功能的工作原理

如果对象实现Accessibleinterface,则可以访问该对象。 Accessibleinterface仅定义一种方法getAccessibleContext,该方法返回AccessibleContext对象。 AccessibleContext对象是包含可访问对象的可访问信息的中介。下图显示了辅助技术如何从可访问对象获取可访问上下文并向其查询信息:

辅助技术如何从可访问对象中获取信息。

AccessibleContext是抽象类,用于定义可访问对象必须提供的有关其自身的最小信息集。最小集包括名称,描述,角色,状态集等。为了将其可访问对象标识为具有特定功能,可访问上下文可以实现一个或多个interface,如Accessible Interfaces表中所示。例如,JButton实现AccessibleActionAccessibleValueAccessibleTextAccessibleExtendedComponentJButton不必实现AccessibleIcon,因为这是通过附加在按钮上的ImageIcon实现的。

由于JComponent类本身未实现Accessibleinterface,因此无法访问其直接子类的实例。如果编写直接从JComponent继承的自定义组件,则需要显式使其实现Accessibleinterface。 JComponent确实具有一个称为AccessibleJComponent的可访问上下文,该上下文实现AccessibleComponentinterface并提供最少的可访问信息。通过创建AccessibleJComponent的子类并覆盖重要方法,可以为自定义组件提供可访问的上下文。 使自定义组件可访问显示了执行此操作的两个示例。

所有其他标准 Swing 组件都实现Accessibleinterface,并具有可访问的上下文,该上下文适当地实现了一个或多个前面的interface。 Swing 组件的可访问上下文被实现为内部类,并具有以下样式的名称:

Component.AccessibleComponent

如果您创建标准 Swing 组件的子类,并且您的子类与其父类实质上不同,则应为其提供一个自定义的可访问上下文。最简单的方法是创建超类的可访问上下文类的子类,并根据需要重写方法。例如,如果创建与JLabel实质上不同的JLabel子类,则JLabel子类应包含扩展AccessibleJLabel的内部类。下一部分将使用JComponent子类扩展AccessibleJComponent的示例说明如何执行此操作。

使自定义组件可访问

滚动演示程序使用三个自定义组件类。 ScrollablePictureJLabel的子类,并且CornerRule都是JComponent的子类。

ScrollablePicture类完全依赖于从JLabelJLabel.AccessibleJLabel继承的可访问性。创建ScrollablePicture实例的代码为可滚动图片设置工具提示文本。上下文将工具提示文本用作组件的可访问描述。 AccessibleJLabel提供此行为。

Corner类的可访问版本仅包含足以使其实例可访问的代码。我们通过将粗体显示的代码添加到原始版本Corner中来实现可访问性支持。

public class Corner extends JComponent implements Accessible {

    protected void paintComponent(Graphics g) {
        //Fill me with dirty brown/orange.
        g.setColor(new Color(230, 163, 4));
        g.fillRect(0, 0, getWidth(), getHeight());
    }

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

    protected class AccessibleCorner extends AccessibleJComponent {
        //Inherit everything, override nothing.
    }
}

此类提供的所有可访问性都继承自AccessibleJComponent。这种方法对Corner来说很好,因为AccessibleJComponent提供了大量合理的默认可访问性信息,并且由于角点并不有趣:它们的存在只是为了占据屏幕上的一点点空间。其他类(例如Rule)需要提供自定义信息。

Rule以与Corner相同的方式为其提供了可访问的上下文,但是该上下文覆盖了两种方法以提供有关组件的角色和状态的详细信息:

protected class AccessibleRuler extends AccessibleJComponent {

    public AccessibleRole getAccessibleRole() {
        return AccessibleRuleRole.RULER;
    }

    public AccessibleStateSet getAccessibleStateSet() {
        AccessibleStateSet states =
            super.getAccessibleStateSet();
        if (orientation == VERTICAL) {
            states.add(AccessibleState.VERTICAL);
        } else {
            states.add(AccessibleState.HORIZONTAL);
        }
        if (isMetric) {
            states.add(AccessibleRulerState.CENTIMETERS);
        } else {
            states.add(AccessibleRulerState.INCHES);
        }
        return states;
    }
}

AccessibleRole是对象的枚举,用于标识 Swing 组件可以扮演的角色。它包含 预定义的角色,例如标签,按钮等。我们示例中的标尺不能很好地适应任何 预定义的角色,因此该程序在AccessibleRole的子类中发明了一个新的标尺:

class AccessibleRuleRole extends AccessibleRole {
    public static final AccessibleRuleRole RULER
        = new AccessibleRuleRole("ruler");

    protected AccessibleRuleRole(String key) {
        super(key);
    }

    //Should really provide localizable versions of these names.
    public String toDisplayString(String resourceBundleName,
                                  Locale locale) {
        return key;
    }
}

任何具有状态的组件都可以通过重写getAccessibleStateSet方法来向辅助技术提供状态信息。规则有两组状态:其方向可以是垂直或水平,其度量单位可以是厘米或英寸。 AccessibleState是 预定义状态的枚举。该程序将其 预定义状态用于垂直和水平方向。由于AccessibleState不包含厘米和英寸,因此程序将创建一个子类以提供适当的状态:

class AccessibleRulerState extends AccessibleState {
    public static final AccessibleRulerState INCHES
        = new AccessibleRulerState("inches");
    public static final AccessibleRulerState CENTIMETERS
        = new AccessibleRulerState("centimeters");

    protected AccessibleRulerState(String key) {
        super(key);
    }

    //Should really provide localizable versions of these names.
    public String toDisplayString(String resourceBundleName,
                                  Locale locale) {
        return key;
    }
}

您已经了解了如何实现两个简单组件的可访问性,这两个组件仅用于在屏幕上绘制自身。做更多事情的组件,例如响应鼠标或键盘事件,需要提供更详尽的可访问上下文。您可以通过深入研究 Swing 组件的源代码来找到实现可访问上下文的示例。

可访问性 API

本节中的表仅涵盖可访问性 API 的一部分。有关可访问性 API 的更多信息,请参阅accessibility package中有关类和包的 API 文档。另外,请参阅 API 文档以获取各个 Swing 组件的可访问上下文。

支持辅助功能的 API 分为以下几类:

命名和链接组件

MethodPurpose
getAccessibleContext().setAccessibleName(String)

getAccessibleContext().setAccessibleDescription(String)
(JComponentAccessible对象上)
提供可访问对象的名称或描述。
void setToolTipText(String)
(*在JComponent *中)
设置组件的工具提示。如果不设置描述,那么许多可访问上下文都将工具提示文本用作可访问描述。
void setLabelFor(Component)
(*在JLabel *中)
将标签与组件相关联。这告诉辅助技术标签描述了另一个组件。
void setDescription(String)
(*在ImageIcon *中)
提供图像图标的描述。

使自定义组件可访问

interface或类Purpose
Accessible

(一个interface)
实现此interface的组件是可访问的。 JComponent的子类必须明确实现此 Object。
AccessibleContext
JComponent.AccessibleJComponent
(抽象类及其子类)
AccessibleContext定义了可访问对象所需的最少信息集。每个 Swing 组件的可访问上下文是此子组件的子类,并按所示命名。例如,JTree的可访问上下文是JTree.AccessibleJTree。为了提供定制的可访问上下文,定制组件应包含一个内部类,该内部类是AccessibleContext的子类。有关更多信息,请参见使自定义组件可访问
AccessibleRole
AccessibleStateSet
(* classes *)
分别定义AccessibleContext对象的getAccessibleRolegetAccessibleStateSet方法返回的对象。
AccessibleRelation
AccessibleRelationSet
定义实现此interface的组件与一个或多个其他对象之间的关系。

Accessible Interfaces

InterfacePurpose
AccessibleAction指示对象可以执行操作。通过实现此interface,可访问上下文可以提供有关可访问对象可以执行哪些操作的信息,并可以告诉可访问对象执行这些操作。
AccessibleComponent指示可访问对象在屏幕上存在。通过此interface,可访问对象可以提供有关其大小,位置,可见性等的信息。所有标准 Swing 组件的可访问上下文都直接或间接实现此interface。自定义组件的可访问上下文应执行相同的操作。首选AccessibleExtendedComponent方法。
AccessibleEditableText指示可访问对象显示可编辑文本。除了可从其superinterfaceAccessibleText获得的信息之外,还提供了剪切,粘贴,删除,选择和插入文本的方法。
AccessibleExtendedComponent除了可从其superinterfaceAccessibleComponent获得的信息之外,还提供了用于获取键绑定,边框文本和工具提示文本的方法。
AccessibleExtendedTable除了可从其superinterfaceAccessibleTable获得的信息之外,还提供了在索引与其行或列之间进行转换的方法。
AccessibleHypertext指示可访问对象包含超链接。通过此interface,可访问对象可以提供有关其链接的信息,并允许遍历它们。
AccessibleIcon指示可访问对象具有关联的图标。提供的方法返回有关图标的信息,例如大小和描述。
AccessibleKeyBinding表示可访问对象支持一个或多个可用于选择对象的键盘快捷键。提供的方法返回给定对象的键绑定。
AccessibleSelection指示可访问对象可以包含选择。实现此interface的可访问上下文可以报告有关当前选择的信息,并且可以修改选择。
AccessibleTable指示可访问对象将数据呈现在二维数据对象中。通过此interface,可以提供有关表的信息,例如表标题,行和列的大小,描述和名称。首选AccessibleExtendedTable方法。
AccessibleText指示可访问对象显示文本。此interface提供了用于返回全部或部分文本,应用于其的属性以及有关文本的其他信息(例如文本 Long 度)的方法。
AccessibleValue指示对象具有数值。通过此interface,可访问对象提供有关其当前值以及其最小值和最大值的信息。

使用辅助功能 API 的示例

下表列出了一些对辅助技术有良好支持的示例。

ExampleWhere DescribedNotes
AccessibleScrollDemoThis section包含两个实现Accessibleinterface的自定义组件。要查看此程序的较难访问版本,请参见如何使用滚动窗格
ButtonDemo如何使用通用按钮 API使用三个按钮。通过按钮文本,助记符和工具提示支持辅助功能。