在本文的大部分篇幅中,我们讨论了如何使 Swing 应用程序可访问,但您如何验证 GUI 的可访问性呢?测试复杂 GUI 上的每个组件是费时的,并需要您手头有 AT 阅读器。Sun Microsystems 提供了帮助您不用 AT 阅读器即可测试 GUI 的工具,但这些工具需要大量人工交互才能有效实施。
为了解决这一问题, AccessibilityUtils 提供了指出可能的或实际遗漏的可访问性信息的报告框架。 public static AccessibilityUtils.output() 方法使用 com.ibm.wac.Outputter 实现来生成报告,如下所示:
2 void output(Component c, OutputStream os, Outputter out);
3 void output(Component c, String fileName, Outputter out) throws
4 IOException;
5
output 方法的实现如清单 15 所示:
清单 15. output() 方法实现
2 if ( out.isEnabled() ) {
3 out.begin(pw);
4 try {
5 outputWorker(0, new HashSet(), c, pw, out);
6 }
7 finally {
8 out.end(pw);
9 }
10 }
11 }
12 protected static void outputWorker(
13 int level, Set seen, Component c, PrintWriter pw, Outputter out) {
14 out.beginLevel(level, pw);
15 try {
16 if ( seen.add(c) ) { // only do the first time seen
17 // output self
18 out.beginObject(level, c, pw);
19 try {
20 out.identifyObject(level, c, pw);
21 out.recommendEol(level, pw);
22 out.outputComponent(level, c, pw);
23 if ( c instanceof Accessible ) {
24 out.recommendEol(level, pw);
25 out.outputAccessible(level, (Accessible)c, pw);
26 }
27 out.recommendEol(level, pw);
28 // output children (if any)
29 if ( c instanceof Container ) {
30 Component[] components =
31 ((Container)c).getComponents();
32 if ( components.length == 0 ) {
33 out.emptyGroup(level, pw);
34 }
35 else {
36 out.beginGroup(level, pw);
37 for ( int i = 0;
38 i <
39 components.length; i++ ) {
40 out.separateGroupMembers(level, i, pw);
41 out.identifyGroupMember(level, i, pw);
42 Component xc = components[i];
43 if ( xc instanceof JComponent ) {
44 outputWorker(level + 1, seen,
45 (JComponent)xc, pw, out);
46 }
47 else {
48 out.outputObject(level, xc, pw);
49 }
50 }
51 out.endGroup(level, pw);
52 out.recommendEol(level, pw);
53 }
54 }
55 }
56 finally {
57 out.endObject(level, c, pw);
58 }
59 }
60 else {
61 out.outputObject(level, c, pw);
62 out.recommendEol(level, pw);
63 }
64 }
65 finally {
66 out.endLevel(level, pw);
67 }
68 }
69
代码注释:
output 方法仅报告指定的组件(通常是 JFrame 、 JDialog 或 JPanel )。如果您的应用程序由多个框架或对话框组成,则您需要为每个框架或对话框调用 output() 方法,以全面地了解应用程序的情况。
任何组件都可以被报告,但它应该是 JComponent 。通常使用优异的组件(如 JFrame ),但较低级别的或动态的组件(如弹出式 JDialog )也可以被报告。
输出器定义了下列方法:
清单 16. 由输出器定义的方法
2 // signal report begin/end
3 void begin(PrintWriter pw);
4 void end(PrintWriter pw);
5 // signal level begin/end
6 void beginLevel(int level, PrintWriter pw);
7 void endLevel(int level, PrintWriter pw);
8 // signal object begin/end
9 void beginObject(int level, Object c, PrintWriter pw);
10 void endObject(int level, Object c, PrintWriter pw);
11 // report object identity
12 void identifyObject(int level, Object c, PrintWriter pw);
13 // report object
14 void outputObject(int level, Object c, PrintWriter pw);
15 void outputComponent(int level, Component c, PrintWriter pw);
16 void outputAccessible(int level, Accessible a, PrintWriter pw);
17 // optional indent report
18 String indent(int level);
19 String indent(int level, String pad);
20 // optionally end report line
21 void recommendEol(int level, PrintWriter pw);
22 // signal group (i.e, container) processing
23 void beginGroup(int level, PrintWriter pw);
24 void separateGroupMembers(int level, int index, PrintWriter pw);
25 void identifyGroupMember(int level, int index, PrintWriter pw);
26 void endGroup(int level, PrintWriter pw);
27 void emptyGroup(int level, PrintWriter pw);
28
与 SAX XML 解析器的工作方式类似,当由方法名表示的事件出现时,由 AccessibilityUtils.output() 方法调用这些方法。 AccessibleUtils 的这个示例中包括下列输出器:
TextOutputter
生成简单的文本格式报告。子集示例是:
2 Component(id=button5,
3 text=,
4 toolTipText=Create a new document,
5 value=?,
6 mnemonic=78,
7 ...) ** others omitted for brevity **
8 Accessible(name=New,
9 role=push button,
10 description=Create a new document,
11 action=javax.swing.JButton$AccessibleJButton@1bd8bf1d,
12 value=javax.swing.JButton$AccessibleJButton@1bd8bf1d,
13 text=null,
14 table=null,
15 relationSet=,
16 ...) ** others omitted for brevity **
17
该文本(不包括所有封装)是由以下代码生成的:
2
HtmlOutputter
生成浏览器中显示的 HTML 报告。 图 3中显示了子集示例。该 HTML 是由下列代码生成的:
2 HtmlOutputter(HtmlOutputter.defaultHeader("Accessibility Demo 1")));
3
XmlOutputter
生成允许进一步处理的 XML 报告,譬如由 XSLT 样式表处理。子集示例如下:
2 desc="javax.swing.JButton:1588FF1D-button6"
3 pdesc="javax.swing.JToolBar:17FAFF1D-toolBar0">
4 <fields>
5 <field context="self" type="java.lang.String">
6 <name>name</name>
7 <value status="ok">button6</value>
8 </field>
9 <field context="self" type="java.lang.String">
10 <name>text</name>
11 <value status="ok"></value>
12 </field>
13 <field context="self" type="java.lang.String">
14 <name>toolTipText</name>
15 <value status="ok">Open an existing document</value>
16 </field>
17 <field context="self" type="java.lang.String">
18 <name>value</name>
19 <value status="warning">--</value>
20 </field>
21 <field context="self" type="java.lang.Integer">
22 <name>mnemonic</name>
23 <value status="ok">79</value>
24 </field>
25 <field context="accessible" type="java.lang.String">
26 <name>name</name>
27 <value status="ok">Open</value>
28 </field>
29 <field context="accessible"
30 type="javax.accessibility.AccessibleRole">
31 <name>role</name>
32 <value status="ok">push button</value>
33 </field>
34 <field context="accessible" type="java.lang.String">
35 <name>description</name>
36 <value status="ok">Open an existing document</value>
37 </field>
38 <field context="accessible"
39 type="javax.swing.JButton$AccessibleJButton">
40 <name>action</name>
41 <value status="ok">
42 javax.swing.JButton$AccessibleJButton@1a05bf1d
43 </value>
44 </field>
45 <field context="accessible"
46 type="javax.swing.JButton$AccessibleJButton">
47 <name>value</name>
48 <value status="ok">
49 javax.swing.JButton$AccessibleJButton@1a05bf1d
50 </value>
51 </field>
52 <field context="accessible">
53 <name>text</name>
54 <value status="warning">null</value>
55 </field>
56 <field context="accessible">
57 <name>table</name>
58 <value status="warning">null</value>
59 </field>
60 <field context="accessible"
61 type="javax.accessibility.AccessibleRelationSet">
62 <name>relationSet</name>
63 <value status="ok"></value>
64 </field>
65 </fields>
66 <!-- empty container -->
67 </container>
68
该 XML 输出(未缩排)是由以下代码生成的:
2 XmlOutputter(XmlOutputter.defaultHeader("Accessibility Demo 1")));
3
该 XML 的 DTD 是:
2 <!ELEMENT gui (component|container)>
3 <!ATTLIST gui xmlns CDATA #IMPLIED>
4 <!ELEMENT component (fields?)>
5 <!ATTLIST component desc CDATA #REQUIRED
6 pdesc CDATA #IMPLIED
7 level CDATA #IMPLIED
8 >
9 <!ATTLIST container desc CDATA #REQUIRED
10 pdesc CDATA #IMPLIED
11 level CDATA #IMPLIED
12 >
13 <!ELEMENT container (fields?,children?)>
14 <!ELEMENT children (component|container)*>
15 <!ELEMENT fields (field)*>
16 <!ELEMENT field (name,value)>
17 <!ATTLIST field context (self|accessible) "self"
18 type CDATA #IMPLIED
19 >
20 <!ELEMENT name (#PCDATA)>
21 <!ELEMENT value (#PCDATA)>
22 <!ATTLIST value status (ok|warning|error) "ok">
23
请注意 DTD 中的 status 属性。它包含关于字段值性质的信息。已定义了下列 status 值:
ok:该值是满足要求的。
warning:该值丢失了或可疑,并有可能导致 AT 不正确地处理组件。
error:该值丢失或错误,并很可能导致 AT 不正确地处理组件。
本文中显示的输出器都是简单的示例。通常,人们将构建格式化更多信息并且可能扩展复杂类型(如 ImageIcon )输出的显示输出器(如 图 3中所示)。
报告框架的另一种方案是将验证代码添加到 AccessibleUtils.output() 方法。如果遗漏了任何组件上必需的可访问信息,这个代码将抛出异常而不是以特定的报告格式报告遗漏的信息(尽管,实际上这种技术在非容器组件上工作得最好)。
添加验证代码可以帮助您更迅速地捕获可访问性错误,而不必检查整个报告。此外,适当地使用验证代码,报告生成过程将充当一种验证测试用例,从而使提供 GUI 实现时仍未设置所有必需的可访问性信息的可能性大大降低。有关此类异常代码的示例,请参阅 清单 7 中的 setAccessibleValues() 方法。请注意 setAccessibleValues 要求每个组件都有一个名称。
结束语
在本文中,您已经了解了如何将可访问性值添加到组成 GUI 的 Swing 组件。在此过程中,您逐渐熟悉了可访问性标准,该标准是根据 1998 年美国康复法案 508 条款的修正案建立的。本文还为您介绍了实用程序方法的示例集,它消除了设置必需的可访问性值所涉及的大量重复劳动。
可访问性工具箱中的实用程序处理可访问 GUI 开发过程中的以下方面:
强制必需的值
验证值
掩盖 GUI 组件设置可访问值的不同方法之间的差异
提供易于编码的帮助,如缺省值和国际化支持选项
帮助确保可访问信息的一致格式(如措辞风格)
可访问性工具箱还提供了可扩展的报告框架,它有助于您验证 GUI 的可访问组件。您已经了解了如何使用框架来生成关于组件层次结构的可访问状态的报告。通过构建定制报告生成器(或 XML 报告处理器),您可以构造不同类型的报告以验证应用程序中的组件。
仔细地使用您在本文中学到的技术,您可以着手为视力和运动有残疾的人士构建更多可访问的应用程序了。
请下载本文的 完整源代码。