文档

Java™教程
隐藏目录
如何支持辅助技术
路径:使用Swing创建GUI
课程:使用其他Swing功能

如何支持辅助技术

你可能会想知道辅助技术到底是什么,以及为什么你应该关心。主要而言,辅助技术的存在是为了让永久或临时残疾的人能够使用计算机。例如,如果你患上了腕管综合症,你可以使用辅助技术完成工作而不用动手。

辅助技术-语音接口、屏幕阅读器、替代输入设备等-不仅对残疾人有用,对于在非办公环境中使用计算机的人也很有用。例如,如果你被困在交通堵塞中,你可以使用辅助技术通过语音输入和输出来查看电子邮件。为辅助技术提供信息的方式也可以用于其他工具,如自动化GUI测试工具和触摸屏等输入设备。辅助技术通过使用在javax.accessibility包中定义的可访问性API从组件获取信息。

由于对可访问性API的支持已经内置到Swing组件中,即使你什么特别的操作都没有做,你的Swing程序可能也能很好地与辅助技术配合使用。例如,辅助技术可以自动获取以下代码设置的文本信息:

JButton button = new JButton("我是一个Swing按钮!");
label = new JLabel(labelPrefix + "0    ");
label.setText(labelPrefix + numClicks);
JFrame frame = new JFrame("SwingApplication");

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

使你的程序与辅助技术顺畅运行非常容易,在美国可能是联邦法律的要求。

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

支持可访问性的规则

以下是你可以做的一些事情,以使你的程序在辅助技术下尽可能地正常工作:

测试可访问性

配套的可访问性工具示例可以让您了解您的程序的可访问性情况。有关获取这些工具的说明,请参阅Java SE桌面可访问性主页。按照可访问性工具文档中的说明,设置Java虚拟机(VM)以自动运行一个或多个工具。

让我们使用一个可访问性工具来比较我们的演示程序的原始版本和应用了支持可访问性规则的版本。这是一个名为ScrollDemo的程序的图片。

原始的、不可访问的ScrollDemo的快照。

试试这个: 
  1. 点击“启动”按钮,使用Java™ Web Start来运行ScrollDemo下载JDK 7或更高版本)。或者,如果想自己编译和运行示例,请查看示例索引

    启动ScrollDemo示例
  2. 接下来,点击“启动”按钮,使用Java™ Web Start来运行AccessibleScrollDemo下载JDK 7或更高版本)。或者,如果想自己编译和运行示例,请查看示例索引启动AccessibleScrollDemo示例
  3. 将两个版本并排比较。唯一明显的区别是在可访问版本中,“cm”切换按钮和照片都有工具提示。

  4. 现在在一个名为Monkey的辅助工具下运行这两个版本。请注意,当辅助工具已被下载并在accessibility.properties文件中配置好时,当你点击运行ScrollDemo和AccessibleScrollDemo的链接(在步骤1和2中)时,Monkey窗口会自动弹出。

    如果Monkey窗口在启动时没有出现,可能是因为Java Web Start使用的VM版本中没有accessibility.properties文件。你可以通过运行Java Web Start的应用程序管理器并选择文件 > 首选项 > Java来更改使用的VM。

  5. 请注意,当Monkey窗口弹出时,你需要选择文件 > 刷新树以查看在Accessible Tree下显示的信息。然后,你可以通过连续点击每个文件夹图标旁边的水平图标来展开树。当树被展开后,你可以看到各个组件的详细信息。在原始版本中无法访问的自定义组件(规则和角落)在修改后的版本中可以访问。这对辅助技术来说可能会有很大的差异。


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

在不可访问版本的ScrollDemo上运行的Monkey的动画。

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

首先要注意的是,即使在ScrollDemo中没有显式支持,Monkey也能够发现关于程序中各种组件的大量信息。大多数组件及其子级出现在树中。然而,大多数组件的名称为空(null),这是相当不方便的。描述也是空的。

进一步的问题出现在程序的自定义组件中。两个标尺是不可访问的,因此它们不包括在可访问树中。包含标尺的视口被显示为叶节点,因为它们没有可访问的子级。可访问树中也缺少自定义角落。

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

在可访问版本的ScrollDemo上运行的Monkey的动画。


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

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

AccessibleScrollDemo截图。

当选择了一个项目后,您可以使用Monkey的面板菜单来打开四个不同的面板之一,这些面板让您与所选组件进行交互。选择面板>可访问API面板会打开一个类似下图所示的面板。该面板显示了在AccessibleContext基类和AccessibleComponent接口中定义的方法提供的信息。

Column Header的可访问API面板。

Monkey还有其他三个面板:

可访问性实用工具示例非常方便,可用作测试工具,并可以让您了解程序中的组件的可访问性。但是,即使您的组件在Monkey或其他示例中表现良好,它们仍然可能不完全可访问,因为Monkey和其他示例只能使用Accessibility API的某些部分。

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

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

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

public AccessibleScrollDemo() {
    // 获取要使用的图片。
    ImageIcon bee = createImageIcon("images/flyingBee.jpg",
                      "一只飞行的蜜蜂的照片。");

    // 创建行和列的标题。
    columnView = new Rule(Rule.HORIZONTAL, true);
    if (bee != null) {
        columnView.setPreferredWidth(bee.getIconWidth());
    } else {
        columnView.setPreferredWidth(320);
    }
    columnView.getAccessibleContext().setAccessibleName("列标题");
    columnView.getAccessibleContext().
            setAccessibleDescription("显示用于测量滚动窗格客户端的水平标尺。");
    rowView = new Rule(Rule.VERTICAL, true);
    if (bee != null) {
        rowView.setPreferredHeight(bee.getIconHeight());
    } else {
        rowView.setPreferredHeight(480);
    }
    rowView.getAccessibleContext().setAccessibleName("行标题");
    rowView.getAccessibleContext().
            setAccessibleDescription("显示用于测量滚动窗格客户端的垂直标尺。");

    // 创建角落。
    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("在英寸和厘米之间切换标尺的单位。");
    buttonCorner.add(isMetric); //使用默认的FlowLayout
    buttonCorner.getAccessibleContext().
                 setAccessibleName("左上角");

    String desc = "出于美观原因,用颜色填充滚动窗格的角落。";
    Corner lowerLeft = new Corner();
    lowerLeft.getAccessibleContext().
              setAccessibleName("左下角");
    lowerLeft.getAccessibleContext().setAccessibleDescription(desc);

    Corner upperRight = new Corner();
    upperRight.getAccessibleContext().
               setAccessibleName("右上角");
    upperRight.getAccessibleContext().setAccessibleDescription(desc);
    
    // 设置滚动窗格。
    picture = new ScrollablePicture(bee,
                                    columnView.getIncrement());
    picture.setToolTipText(bee.getDescription());
    picture.getAccessibleContext().setAccessibleName(
                                     "滚动窗格客户端");

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

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

    // 理论上,为了支持国际化,你应该将UPPER_LEFT_CORNER更改为UPPER_LEADING_CORNER,
    // LOWER_LEFT_CORNER更改为LOWER_LEADING_CORNER,以及
    // UPPER_RIGHT_CORNER更改为UPPER_TRAILING_CORNER。但是,在实践中,
    // bug #4467063使得这是不可能的(至少在1.4.0中)。
    pictureScrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER,
                                buttonCorner);
    pictureScrollPane.setCorner(JScrollPane.LOWER_LEFT_CORNER,
                                lowerLeft);
    pictureScrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER,
                                upperRight);

    // 将其放在此面板中。
    setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
    add(pictureScrollPane);
    setBorder(BorderFactory.createEmptyBorder(20,20,20,20));
}

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

概念:无障碍功能的工作原理

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

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

AccessibleContext是一个抽象类,定义了可访问对象必须提供的最小信息集合。最小集合包括名称、描述、角色、状态集等。为了标识可访问上下文具有特定功能,可访问上下文可以实现如下所示的一个或多个接口。例如,JButton实现了AccessibleActionAccessibleValueAccessibleTextAccessibleExtendedComponent。对于JButton来说,不需要实现AccessibleIcon,因为按钮附加的ImageIcon已经实现了该接口。

因为JComponent类本身不实现Accessible接口,所以它的直接子类的实例是不可访问的。如果您编写直接继承自JComponent的自定义组件,则需要显式使其实现Accessible接口。 JComponent确实有一个可访问上下文,称为AccessibleJComponent,它实现了AccessibleComponent接口并提供了最少量的可访问信息。您可以通过创建AccessibleJComponent的子类并覆盖重要方法为自定义组件提供可访问上下文。 使自定义组件可访问显示了两个示例。

所有其他标准的Swing组件实现了Accessible接口,并且具有实现上述接口中的一个或多个的可访问上下文。Swing组件的可访问上下文被实现为内部类,其命名风格如下:

Component.AccessibleComponent

如果您创建了一个标准Swing组件的子类,并且您的子类与其超类有很大的区别,则应为其提供自定义的可访问上下文。最简单的方法是创建超类可访问上下文类的子类,并根据需要覆盖方法。例如,如果您创建了一个与JLabel有很大区别的JLabel子类,则您的JLabel子类应包含一个继承AccessibleJLabel的内部类。下一节将展示如何使用示例来完成此操作,其中JComponent子类扩展AccessibleJComponent。

使自定义组件可访问

滚动演示程序使用了三个自定义组件类。ScrollablePicture是JLabel的子类,而Corner和Rule都是JComponent的子类。

ScrollablePicture类完全依赖于通过JLabel和JLabel.AccessibleJLabel继承的可访问性。创建ScrollablePicture实例的代码设置了可滚动图片的工具提示文本。工具提示文本将作为组件的可访问描述由上下文使用。这个行为由AccessibleJLabel提供。

Corner类的可访问版本只包含足够的代码使其实例可访问。我们通过将加粗显示的代码添加到Corner的原始版本中来实现辅助功能支持。

public class Corner extends JComponent implements Accessible {

    protected void paintComponent(Graphics g) {
        //使用脏棕橙色进行填充。
        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 {
        //继承所有内容,不进行任何重写。
    }
}

这个类提供的所有辅助功能都继承自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);
    }

    //实际上应该提供这些名称的本地化版本。
    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);
    }

    //实际上应该提供这些名称的本地化版本。
    public String toDisplayString(String resourceBundleName,
                                  Locale locale) {
        return key;
    }
}

您已经看到如何为两个简单的组件实现可访问性,这些组件只存在于屏幕上。更多功能的组件,例如响应鼠标或键盘事件的组件,需要提供更详细的可访问上下文。您可以在Swing组件的源代码中找到实现可访问上下文的示例。

可访问性API

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

支持可访问性的API分为以下几类:

命名和链接组件
方法 目的
getAccessibleContext().setAccessibleName(String)
getAccessibleContext().setAccessibleDescription(String)
JComponentAccessible对象上
为可访问对象提供名称或描述。
void setToolTipText(String)
JComponent
设置组件的工具提示。如果不设置描述,则许多可访问上下文将使用工具提示文本作为可访问描述。
void setLabelFor(Component)
JLabel
将标签与组件关联起来。这告诉辅助技术一个标签描述另一个组件。
void setDescription(String)
ImageIcon
为图像图标提供描述。


制作自定义组件可访问性
接口或类 目的
Accessible
一个接口
实现此接口的组件是可访问的。JComponent的子类必须显式实现此接口。
AccessibleContext
JComponent.AccessibleJComponent
一个抽象类及其子类
AccessibleContext定义了可访问对象所需的最小信息集。每个Swing组件的可访问上下文都是此类的子类,并按所示命名。例如,JTree的可访问上下文是JTree.AccessibleJTree。为了提供自定义的可访问上下文,自定义组件应该包含一个是AccessibleContext子类的内部类。有关更多信息,请参阅制作自定义组件可访问性
AccessibleRole
AccessibleStateSet
分别定义AccessibleContext对象的getAccessibleRolegetAccessibleStateSet方法返回的对象。
AccessibleRelation
AccessibleRelationSet
定义实现此接口的组件与一个或多个其他对象之间的关系。


可访问的接口AccessibleActionAccessibleComponentAccessibleExtendedComponentAccessibleEditableText
AccessibleTextAccessibleExtendedComponent
AccessibleComponentAccessibleExtendedTable
AccessibleTableAccessibleHypertextAccessibleIconAccessibleKeyBinding
AccessibleSelectionAccessibleTableAccessibleExtendedTableAccessibleTextAccessibleValue

使用辅助功能API的示例

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

示例 描述位置 备注
AccessibleScrollDemo 本节 包含实现了Accessible接口的两个自定义组件。要查看较不易访问的版本,请参见使用滚动窗格
ButtonDemo 使用通用按钮API 使用三个按钮。通过按钮文本、助记符和工具提示支持辅助功能。

上一页: 如何使用Swing计时器
下一页: 如何使用焦点子系统