技术开发 频道

为可访问性编码

  为复杂 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 的精简版本

1public static class AccessibleValues {
2       public String name;              // component's name/id
3       public String shortDescription;  // == accessible name
4       public String longDescription;   // == accessible description
5       public String toolTip;           // component's tool tip
6       public String text;              // component's text
7       public String borderText;        // component border's text
8       public int    mnemonic;          // component's mnemonic
9       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

1 JButton b = new JButton();
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()

1 protected static final Class[] _sType = {String.class};
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 用法示例

1 JButton setupButton(String name, String action, int vKey) {
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 有以下形式:

1 String formatText(String pattern, String args, String delims);
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
0
相关文章