为复杂 GUI 中的每个组件设置值很乏味冗长,这项工作经常会导致错误或完全地遗漏某些重要步骤。为了纠正这一点,我创建了一个可访问性工具箱,这是一组实用程序方法,它们可以显著减少在您的 GUI 中提供可访问信息时需要的“set”方法的数量。
下列实用程序方法是 com.ibm.wac.AccessibleUtils 类的 public static 成员:
setAccessibleValues(ResourceBundle rb, Accessible a, AccessibleValues av) 设置最常用的可访问组件值。
Accessible setMemberRelationship(Accessible group, Collection members) 创建组组件和由集合定义的可访问对象集之间的成员关系。
Accessible setMemberRelationship(Accessible group, Accessible[] members) 创建组组件和由数组定义的可访问对象之间的成员关系。
Accessible setLabelRelationship(Accessible label, Accessible target) 创建可访问目标和标签之间的关系。 setLabelRelationship 通常用来为自身没有适当可访问信息的组件提供可访问信息。它还允许通过键盘助记符访问那些不支持键盘助记符的组件(例如, JTextField )。
在后面几节中,我们将仔细研究该工具箱的 setAccessibleValues() 方法,以了解它是如何辅助创建和定义大量 GUI 组件的。在详细描述对 关系和 助记符支持章节中,您还会大致了解到其它实用程序方法(以及它们的助手方法)是如何工作的。
使用 setAccessibleValues
setAccessibleValues() 方法有三个参数。 ResourceBundle 参数(出自 java.util )允许对国际化的自动支持;如果不需要进行文本转换,它将为 null。 Accessible 参数由 setAccessibleValues() 方法更新。 AccessibleValues 参数(出自 com.ibm.wac )提供了最常用的可访问属性。您可以自由地将更常用的组件属性添加到这个集合中。
清单 5 显示了 AccessibleValues 类的精简版本:
清单 5. AccessibleValues 的精简版本
public static class AccessibleValues {2
public String name; // component's name/id3
public String shortDescription; // == accessible name4
public String longDescription; // == accessible description5
public String toolTip; // component's tool tip6
public String text; // component's text7
public String borderText; // component border's text8
public int mnemonic; // component's mnemonic9
public AccessibleValues(String name,10
String text,11
String shortDescription<,12
String longDescription<,13
String toolTip<,14
int mnemonic<,15
String borderText>>>>) {...}16
}17

并非所有组件都需要这个类中所有的值,因此它提供了实现可选参数的多个构造器。当使用 setAccessibleValues 方法时,最好使用这个方法而不是组件的普通方法来设置组件的文本(如果有的话)。清单 6 说明了如何使用 setAccessibleValues 方法设置按钮组件的值:
清单 6. 按钮组件的 setAccessibleValues
2 AccessibleUtils.setAccessibleValues(null, (Accessible)b,
3 new AccessibleUtils.AccessibleValues(
4 "button1",
5 "OK",
6 "OK Button",
7 "Activate to commit changes and continue",
8 "Commit changes",
9 (int)'O');
尽管清单 6 中的代码与 清单 2中的按钮序列所做的事情相同,但它有下列优点:
如果遗漏了必需的参数,则 setAccessibleValues() 语法会强制产生一个错误。
setAccessibleValues() 方法比清单 2 中的按钮序列更简洁(如果将所有参数都放到一行中,该方法甚至只需较少几行就可以了)。
因为调用了一个方法,该方法可以执行额外的处理和验证。
通过转换由 java.util.ResourceBundle 提供的文本,可以自动地支持国际化。
实际使用的实用程序方法
清单 7 显示了 setAccessibleValues() 方法是如何工作的。首先研究代码,然后查看后面的注释。
清单 7. 实际使用的 setAccessibleValues()
2 protected static final Class[] _iType = {Integer.TYPE};
3 :
4 Accessible setAccessibleValues(
5 ResourceBundle rb, Accessible a, AccessibleValues av) {
6 if ( av.name != null ) {
7 throw new NullPointerException(
8 "accessible components require a name");
9 }
10 if ( a instanceof Component ) { // nearly always true
11 ((Component)a).setName(av.name);
12 }
13 if ( av.text != null ) {
14 Method m = resolveMethod(a, "setText", _sType);
15 try {
16 invokeMethod(a, m, new String[] {resolveString(rb, av.text)});
17 }
18 catch ( Exception e ) {
19 throw new AccessibleException(
20 "cannot invoke method setText(String text) - " + a, e);
21 }
22 }
23 if ( av.borderText != null ) {
24 JComponent c = (JComponent)a;
25 Border b = c.getBorder();
26 Border tb = new TitledBorder(resolveString(rb, av.borderText));
27 c.setBorder(b != null ? new CompoundBorder(b, tb) : tb);
28 }
29 if ( av.toolTip != null ) {
30 String text = resolveString(rb, av.toolTip.equalsIgnoreCase("=ld")
31 ? av.longDescription : av.toolTip);
32 if ( a instanceof JComponent ) {
33 ((JComponent)a).setToolTipText(text);
34 }
35 else if ( a instanceof ImageIcon ) {
36 ((ImageIcon)a).setDescription(text);
37 }
38 }
39 if ( av.mnemonic >= 0 ) {
40 Method m = resolveMethod(a, "setMnemonic", _iType);
41 if ( m == null ) {
42 m = resolveMethod(a, "setDisplayedMnemonic", _iType);
43 }
44 try {
45 invokeMethod(a, m, new Integer[] {new Integer(av.mnemonic)});
46 }
47 catch ( Exception e ) {
48 throw new AccessibleException(
49 "cannot invoke method set{Displayed}Mnemonic(int key) - "
50 + a, e);
51 }
52 }
53 if ( av.shortDescription == null ) {
54 throw new NullPointerException(
55 "accessible components require a shortDescription");
56 }
57 if ( av.shortDescription.equalsIgnoreCase("=tt") ) {
58 av.shortDescription = av.toolTip;
59 }
60 if ( av.shortDescription.equalsIgnoreCase("=ld") ) {
61 av.shortDescription = av.longDescription;
62 }
63 if ( av.shortDescription.length() == 0 ) {
64 av.shortDescription = null;
65 }
66 if ( av.shortDescription != null ) {
67 if ( a instanceof ImageIcon ) {
68 ((ImageIcon)a).setDescription(
69 resolveString(rb, av.shortDescription));
70 }
71 }
72 AccessibleContext ac = a.getAccessibleContext();
73 if ( ac == null ) {
74 throw new NullPointerException(
75 "AccessibleContext cannot be null on an Accessible object "
76 + formatClassToken(a));
77 }
78 if ( av.shortDescription != null ) {
79 ac.setAccessibleName(resolveString(rb, av.shortDescription));
80 }
81 if ( av.longDescription != null ) {
82 ac.setAccessibleDescription(
83 resolveString(rb, av.longDescription.equalsIgnoreCase("=tt")
84 ? av.toolTip : av.longDescription));
85 }
86 return a;
87 }
88
代码注释:
在 Swing(以及 AWT)中,每个组件都可以任意地由标识字符串标识;但是, setAccessibleValues() 方法需要一个名称。在本文中您将更进一步地深入理解名称的使用。
如果提供了文本参数,则设置组件的文本。这允许在组件的构造器中省略该文本,以便进行国际化转换。为了不要求组件必须是某种特定类型,用反射(而不是向下类型转换(downcasting))来发现和调用 setText() 方法。
如果提供了边框文本参数,则为组件创建并设置有标题的边框。有标题的边框提供了关于组件组的信息(我们将在本文中进一步讨论组件组)。
如果提供了 ToolTip 文本参数,则设置组件的 ToolTip 或图标的描述。
如果提供了助记符参数,则设置组件的助记符。为了不要求组件必须是某种特定类型,用反射来发现和调用可用的方法。
如果提供了短描述参数,则设置组件的短描述。短描述是 AccessibleName 的另一种说法。可以将其缺省地设置为 ToolTip(通过输入 =tt )或长描述(通过输入 =ld )。短描述是必需的(即它们不能为 null )。允许但不推荐空白短描述。
如果提供了长描述参数,则设置长描述。长描述是 AccessibleDescription 的另一种说法。可以通过输入“ =tt ”将长描述缺省地设置为 ToolTip。
来自朋友的一点帮助
清单 8 显示了一个更实际的 setAccessibleValues() 用法示例,其中 resourceBundle 是实例字段。请注意助手方法的使用。
清单 8. setAccessibleValues 用法示例
2 return (JButton)AccessibleUtils.setAccessibleValues(resourceBundle,
3 (Accessible)new JButton(),
4 new AccessibleUtils.AccessibleValues(
5 idGen.nextId("button"),
6 name,
7 AccessibleUtils.formatText(resourceBundle,
8 "{0} button", name),
9 "=tt",
10 AccessibleUtils.formatText(resourceBundle,
11 "Press to {0}", action),
12 vKey));
13 }
14
setupButton 方法通过封装 setAccessibleValues() 调用,对按钮创建进行了进一步简化和标准化。请注意它使用“ =tt ”将可访问描述设置成了 ToolTip。 IdGenerator.nextId(String base) 是生成唯一名称的实用程序方法。 idGen 是 IdGenerator 的实例。
各种 public static AccessibilityUtils.formatText() 方法通过插入值对字符串进行格式化。 formatText 方法使用了 java.text.MessageFormat 类。 formatText 有以下形式:
2 String formatText(ResourceBundle rb, String pattern, Object[] args);
3 String formatText(ResourceBundle rb, String pattern, String args);
4 String formatText(ResourceBundle rb, String pattern, String args,
5 String delims);
6