IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Java technology  >

Reflexive User Interface Builder 简介

利用 alphaWorks 的新技术简便快捷地构建 Java GUI

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

样例代码


级别: 初级

Barry A. Feigenbaum 博士, Sr. Consulting IT Architect, IBM 
Michael Squillace 博士 (masquill@us.ibm.com), Software Engineer, IBM 

2004 年 8 月 04 日

IBM Reflexive User Interface Builder (RIB)是来自 alphaWorks 的一项新技术,是用来构建和提供 Java AWT/Swing 和 Eclipse SWT GUI 的应用程序和工具包。RIB 指定了一种灵活易用的 XML 标记语言来描述 Java GUI,并为创建这些 GUI 提供了引擎。可以使用 RIB 测试和评估基本的 GUI 布局和功能,或者为应用程序创建和提供 GUI。

不论使用何种小窗口部件集(widget set),用 Java 语言手工编写 GUI 都将是一个单调乏味并且容易出错的过程。使用标记语言来说明 GUI 要容易得多。IBM Reflexive User Interface Builder (RIB) 是一种基于描述性 XML 文档构造和提供 Jave AWT/Swing 和 Eclipse SWT GUI 的应用程序,该 XML 文档是作为脚本引用的。RIB 既是用来描述 Java GUI 的标记语言的规范,也是用来创建和提供这些 GUI 的引擎。可以将 RIB 用作独立的应用程序,来测试和评估基本的 GUI 布局和功能,也可以将其用作 Java 应用程序环境中的库,为应用程序创建和提供 GUI。

本文对 RIB 应用程序进行了简要说明。我们将介绍 RIB 的功能,并使用两个 RIB 脚本示例来说明这些功能。这些示例都是基于 Java 2 Swing 框架来构建 GUI 的,并且所有嵌入代码都是用 Jython 编写的,Jython 是一种允许使用 Python 编程语言的语法及大多数功能的 Java 应用程序(请参阅 参考资料)。现在,RIB 支持将 JavaScript/Rhino、Jython 和 JRuby 用作脚本语言。

RIB 的强大功能

虽然存在其他 XML 脚本驱动 Java GUI 引擎,但是 RIB 方法的能力源于它对 Java Reflection API 的使用,Java Reflection API 允许类进行内省,以便显示其公共字段、构造函数和方法(请参阅 参考资料)。RIB 可以仅基于 XML 文档中的信息来创建和提供 GUI,而 XML 文档则不需要任何 Document Type Definition (DTD) 或 XML 模式。

除了用来创建和提供 GUI 的引擎,RIB 还提供了验证引擎,您可以使用该功能验证组件的属性和组件之间关系。可以为组件指定一组约束,然后确保组件的行为遵守这些约束。例如,RIB 与可用性验证机制封装在一起。这些机制可以提高您快速开发、测试和评估基于 GUI 的应用程序的能力。





回页首


RIB 的基础知识

我们的第一个脚本示例创建了一个有两个文本字段和两个按钮的简单 GUI,如图 1 所示:


图 1. 使用 RIB 构建的简单 GUI
显示名称和电子邮件输入以及 Clear 和 Exit 按钮的简单 GUI

清单 1 包含 RIB 用来生成图 1 中的 GUI 的 XML 文档:


清单 1. 简单 GUI 的 RIB 脚本
<?xml version="1.0"?>
<rib:gui
  xmlns:rib="com.ibm.wac.rgb"
  xmlns="swing"
  rib:scriptlang="jython"
  rib:architecture="swing"
>
  <rib:scripts>
    import javax.accessibility.AccessibleRelation as AccRelation
  </rib:scripts>
  <rib:aliases>
    <rib:alias
      rib:name="BorderLayout"
      rib:value="java.awt.BorderLayout"
    />
    <rib:alias
      rib:name="acName"
      rib:value="!getAccessibleContext!setAccessibleName"
    />
  </rib:aliases>
  <rib:objects>
    <Dimension rib:id="screenDim">300, 150</Dimension>
    <Color rib:id="bkgdColor">224, 224, 255</Color>
  </rib:objects>
  <rib:components>
    <Frame rib:id="mainFrame"
      size="@screenDim"
      title="RGB -- Sample 1 fddlkjlkdlkdflkd"
      background="@bkgdColor"
    >
      <getRootPane>
        <defaultButton button="@clearButton"/>
      </getRootPane>
      <addWindowFocusListener><windowFocusGained>
        nameField.requestFocus()
      </windowFocusGained></addWindowFocusListener>
      <getContentPane>
        <Panel rib:id="infoPanel" rib:constraints="NORTH"
          layout="%BorderLayout"
        >
          <Box rib:constraints="NORTH">
            swing.BoxLayout.X_AXIS
            <horizontalGlue/>
            <Label rib:id="nameLabel"
              text="Name:"
              labelFor="@nameField"
              horizontalAlignment="RIGHT"
            />
            <horizontalStrut width="4"/>
            <TextField rib:id="nameField"
              columns="20"
              toolTipText="Enter your full name"
              focusAccelerator="n"
            >
              <acName name="name input field"/>
              <acRelation rel="{AccRelation(AccRelation.LABELED_BY, nameLabel)}"/>
            </TextField>
            <horizontalStrut width="8"/>
            <Label rib:id="emailLabel"
              text="Email:"
              labelFor="@emailField"
              horizontalAlignment="RIGHT"
            />
            <horizontalStrut width="4"/>
            <TextField rib:id="emailField"
              columns="20"
              toolTipText="Enter your email address"
            >
              <acName name='email input field'/>
              <acRelation rel="{AccRelation(AccRelation.LABELED_BY, emailLabel)}"/>
            </TextField>
            <horizontalGlue/>
          </Box>
          <Box rib:constraints="SOUTH">
            swing.BoxLayout.X_AXIS
            <horizontalGlue/>
            <Button rib:id="clearButton" text="Clear"
              toolTipText="Clear the form fields">
              <mnemonic>
                awt.event.KeyEvent.VK_R
              </mnemonic>
              <addActionListener>
                nameField.text = ""
                emailField.text = ""
              </addActionListener>
            </Button>
            <horizontalStrut width="6"/>
            <Button rib:id="exitButton" text="Exit"
              toolTipText="Exit the app">
              <mnemonic>
                awt.event.KeyEvent.VK_X
              </mnemonic>
              <addActionListener>
                confirm = \
                  swing.JOptionPane.showConfirmDialog(
                  mainFrame, 
                  "Confirm Exit",
                  "Confirm Exit Dialog",
                  swing.JOptionPane.YES_NO_OPTION
                  )
                if confirm == swing.JOptionPane.YES_OPTION:
                  lang.System.exit(0)
              </addActionListener>
            </Button>
            <horizontalGlue/>
          </Box>
        </Panel>
      </getContentPane>
    </Frame>
  </rib:components>
</rib:gui>

本例从 RIB 根 <rib:gui> 标签开始,该标签可以有 4 个子元素: <rib:aliases><rib:objects><rib:components><rib:scripts>

别名

<rib:aliases> 标签组列出了 XML 文档使用的别名。别名有两种使用形式: 标准别名方法别名。在标准别名中,RIB 用别名的值替代名称。这种替代发生在标签名或属性值中。例如,查看 清单 1中的下列别名定义:

<rib:alias rib:name="BorderLayout" rib:value="java.awt.BorderLayout"/>

提供了别名定义后,当 RIB 遇到名称为 BorderLayout 的元素时,就会使用标签的别名值 java.awt.BorderLayout 。RIB 定义了许多标准别名,例如,您可以不定义别名而写入 Button ,并且不用写入 javax.swing.JButton

方法别名中,别名包含以叹号 (!) 开头的值,如清单 1 中的别名定义所示:

<rib:alias rib:name="acName" value="!getAccessibleContext!setAccessibleName"/>

当 RIB 遇到 XML 文档中的方法别名的名称时,它会调用该别名值中列出的每一个方法。当 RIB 遇到 清单 1 的文档中的 acName 时,它会在包含该元素的对象上调用 getAccessibleContext().setAccessibleName(...)

对象

可以使用 RIB 的 <rib:objects> 标签来定义在文档的多个位置上使用的对象。清单 1 中的简单脚本示例定义了两个对象, screenDimbkgdColor

<rib:objects>
    <Dimension rib:id="screenDim">300, 150</Dimension>
    <Color rib:id="bkgdColor">224, 224, 255</Color>
</rib:objects>

为了实例化正在讨论的对象,您还可以将其他对象定义为子对象:

<Dimension rib:id="screenDim">
    <Point>300, 150</Point>
</Dimension>





回页首


创建一个简单的 GUI

清单 1 中的脚本示例创建了一个 GUI,并通过 <Frame> 标签定义了该 GUI。组件定义中的元素将执行下列两项任务之一(按以下顺序进行):

  1. 示例化一个对象。
  2. 调用作为父元素的结果被实例化的对象的方法(或者调用由父元素启动并通过方法调用返回的对象的方法)。

该标签举例说明了步骤 1(即创建了一些对象):

<Frame rib:id="mainFrame" size="@screenDim"
   title="RGB -- Sample 1" background="@bkgdColor">

该标签创建了 javax.swing.JFrame 对象。对象 ID 为 mainFrame ,您可以在 XML 文档中使用该 ID 引用 JFrame ,或者使用该 ID 从其他 Java 类中访问 JFramesizetitlebackground 属性被解释为 JFrame 对象的属性,并会导致 RIB 调用 setSize(Dimension)setTitle(String)setBackground(Color) 方法。注意对以前定义的 screenDimbkgdColor 对象的引用。

该标签举例说明了步骤 2(启动方法调用):

<getContentPane>
   :
</getContentPane>

RIB 调用被父元素实例化了的 JFrame 对象上的 getContentPane() 方法。

此时,RIB 将继续处理脚本,通过尝试实例化对象或尝试调用对象上的方法来处理每个元素。如果对于某些元素,实例化成功,则新形成的对象将被“链接”到父元素对象。如果实例化失败,则需要使用下列算法选择将调用的方法:

  1. 尝试使用元素的精确名称来调用方法。
  2. 接着尝试使用基于元素名称的名称来调用方法。
  3. 尝试将具有给定元素名称的公共字段设置为单个属性或元素文本内容中指定的值。

查看 清单 1的文档中的以下元素:

<Panel rib:id="infoPanel" rib:constraints="NORTH" 
       layout="%BorderLayout">

该元素使用 infoPanelrib:id 实例化了 javax.swing.JPanel 对象。通过定义属性布局并将其值设为 %BorderLayout ,您可以设置 JPanel 的布局,RIB 使用该属性值查找以前定义的别名,并获得值 java.awt.BorderLayoutrib:constraints 属性指明,应该将新形成的 JPanel 放置在通过父元素布局生成的对象的上下文中。 JPanel 的默认 LayoutManagerjava.awt.BorderLayout 对象,RIB 使用该对象解析 rib:constraints 属性的值 java.awt.BorderLayout.NORTH

现在检查 清单 1中的另一个代码块:

<Box rib:constraints="NORTH">swing.BoxLayout.X_AXIS
    <horizontalGlue/>
    <Label rib:id="nameLabel" text="Name:"
       labelFor="@nameField" horizontalAlignment="RIGHT"/> 
    <horizontalStrut width="4"/>
    <TextField rib:id="nameField" columns="20"
      toolTipText="Enter your name" focusAccelerator="n">
       <acName name="name input field"/>
       <acRelation
          rel="{AccRelation(AccRelation.LABELED_BY, nameLabel)}"/>
    </TextField>
      :
</Box>

<Box> 标签内有多个组件:

  • 表达式 swing.BoxLayout.X_AXIS 被用作 Box 构造函数的参数。

  • <horizontalGlue/> 标记,它启动对 Box 对象的 createHorizontalGlue() 方法的调用,如上述算法的步骤 2 所述。

  • <Label> 标签,它实例化了一个 javax.swing.JLabel 对象。 LabeltexthorizontalAlignmentlabelFor 属性被设置。

  • 接下来是 <horizontalStrut> 标签,它促使 RIB 使用参数调用包含一个参数的 Box 对象上的 createHorizontalStrut(int) 方法。

  • JTextField 对象,该对象被实例化,并且其属性也被设置。 JTextField 对象的 rib:id 允许 JLabel 引用这个 JTextField 对象。注意对 <TextField> 元素的子元素中的方法别名的引用。

所有这些对象都被依次添加到父 <Box> 标签实例化的 Box 对象中。





回页首


事件处理程序和可执行代码

在默认情况下,可以将脚本中任何元素的文本内容都作为以特定脚本语言(如 Jython)编写的可执行代码进行处理。请参见 清单 1中 Clear 按钮的代码:

<Button rib:id="clearButton" text="Clear">
    <mnemonic>awt.event.KeyEvent.VK_R</mnemonic>
    <addActionListener>
      nameField.text = ""
      emailField.text = ""
    </addActionListener>
</Button>

setMnemonic(int) 方法是通过 Jython 解释程序评估表达式 awt.event.KeyEvent.VK_R 而返回的值来调用的。您还可以通过在属性值中包含一对大括号({...}),将属性值作为一行表达式来进行处理。例如,您可能需要指定 JPanel 的布局:

<Panel ... layout="{awt.BorderLayout()}" ...>

更有趣的是,通过在 <addActionListener> 标签中包含 Jython 代码(本例中的一些赋值语句),可以将事件处理程序附加到 Clear 按钮中。注意,从代码访问指定的标签( nameField )比较容易一些。因为 ActionListener 只实现了一个方法 —— actionPerformed ,RIB 使用给定的代码来实现该方法。注意,可以将所有 rib:id 值以及许多标准 Jython 和 Java 包都用作预定义变量,从而将它们用于 RIB 脚本。

现在来查看下面的定义,它向根 JFrame 添加了一个 java.awt.event.WindowListener

<addWindowListener>
   <windowClosing>lang.System.exit(0)</windowClosing>
</addWindowListener>

ActionListener 不同, WindowListener 必须实现接口规定的多个方法。该脚本通过包含具有正在实现的方法名称的元素来区别这些方法。

在构建了 清单 1中脚本定义的 GUI 之后,RIB 可以以多种方式提供 GUI。使用 RIB 库的应用程序可以调用 com.ibm.wac.rgb.engine.RgbEngine 类的 show(String) 方法来提供具有给定 ID 的组件。如果从命令行执行 RGBuilder 类,则可以使用 -show-showAll 开关指定要提供的 GUI。





回页首


构建较复杂的 GUI

现在,可以构建由 javax.swing.JTabbedPane 中的多个窗格、按钮面板以及包含菜单和菜单项示例的菜单栏组成的更复杂的 GUI,如图 2 中的 4 个图所示:


图 2a. 显示标签的 TabPane 示例
显示标签的 TabPane GUI 示例

图 2b. 显示输入表单的 TabPane 示例
显示包含姓名、地址、城市、州和邮政编码的输入表单的 TabPane GUI 示例

图 2c. 显示姓名表的 TabPane 示例
显示包含名字、中间名和姓氏三个条目的表的 TabPane GUI 示例

图 2d. 显示节点树的 TabPane 示例
显示打开了多个分支的节点树的 TabPane GUI 示例

虽然图 2 显示的功能非常有限,但它说明了使用 RIB 可以很容易地生成和提供更复杂的 GUI。清单 2 包含了图 2 中的 GUI 的 XML 文档:


清单 2. 复杂 GUI 的 XML 文档
<?xml version="1.0"?>
<rib:gui
  xmlns:rib="com.ibm.wac.rgb"
  rib:scriptlang="jython"
  rib:architecture="swing"
>
  <rib:scripts>
    <rib:script
      rib:name="initScript"
      rib:file="examples/swing/tree.py"
      rib:exec="true"
    />
    <rib:script>
      def getCtrlKeyStroke (keyCode):
        return KeyStroke.getKeyStroke(
          keyCode, InputEvent.CTRL_MASK )
    </rib:script>
  </rib:scripts>
  <rib:aliases>
    <rib:alias
      rib:name="PVLayout"
      rib:value="com.ibm.wac.rgb.swing.PromptedValueLayout"
    />
  </rib:aliases>
  <rib:objects>
    <Dimension rib:id="screenDim">400, 300</Dimension>
    <Color rib:id="bkgdColor">224, 224, 255</Color>
    <Color rib:id="fgColor">255, 0, 255</Color>
    <PVLayout rib:id="pvLayout">
      [10, 10, 10], [5, 5, 5]
    </PVLayout>
  </rib:objects>
  <rib:components>
    <Frame rib:id="mainFrame"
      size="@screenDim"
      title="TabbedPane Sample"
      background="@bkgdColor"
      jMenuBar="{swing.JMenuBar()}"
    >
      <addWindowListener>
        <windowClosing>
        lang.System.exit(0)
        </windowClosing>
      </addWindowListener>
    <getJMenuBar>
      <Menu rib:id="fileMenu">"File"
      <mnemonic>KeyEvent.VK_F</mnemonic>
      <MenuItem
        accelerator="{getCtrlKeyStroke(KeyEvent.VK_N)}"
        text="new"
      />
      <MenuItem
        accelerator="{getCtrlKeyStroke(KeyEvent.VK_O)}"
        text="Open"
      />
      <MenuItem
        accelerator="{getCtrlKeyStroke(KeyEvent.VK_S)}"
        text="Save"
      />
      </Menu>
      <Menu rib:id="editMenu">"Edit"
      <mnemonic>KeyEvent.VK_E</mnemonic>
      <MenuItem
        accelerator="{getCtrlKeyStroke(KeyEvent.VK_X)}"
        text="Cut"
      />
      <MenuItem
        accelerator="{getCtrlKeyStroke(KeyEvent.VK_C)}"
        text="Copy"
      />
      <MenuItem
        accelerator="{getCtrlKeyStroke(KeyEvent.VK_V)}"
        text="Paste"
      />
      </Menu>
    </getJMenuBar>
      <getContentPane>
        <Panel rib:id="topPanel" rib:constraints="CENTER"
          layout="java.awt.BorderLayout"
        >
          <TabbedPane rib:id="tabPane" rib:constraints="CENTER">
          <addTab>
            <rib:arguments>
            <String>"Label"</String>
            <Label rib:id="testLabel"
              text="Label Text"
              horizontalAlignment="CENTER"
              foreground="@fgColor"
            />
            </rib:arguments>
          </addTab>
          <addTab>
            <rib:arguments>
            <String>"Form"</String>
            <Panel rib:id="addrPanel"
              rib:constraints="NORTH"
              layout="@pvLayout"
            >
              <Label rib:id="nameLabel"
                text="Full Name:"
                labelFor="@nameField"
                horizontalAlignment="RIGHT"
              />
              <TextField rib:id="nameField"
                columns="20"
                rib:constraints="@nameLabel"
              />
              <Label rib:id="addrLabel"
                text="Address:"
                labelFor="@addrField"
                horizontalAlignment="RIGHT"
              />
              <TextField rib:id="addrField"
                columns="20"
                rib:constraints="@addrLabel"
              />
              <Label rib:id="cityLabel"
                text="City:"
                labelFor="@cityField"
                horizontalAlignment="RIGHT"
              />
              <TextField rib:id="cityField"
                columns="15"
                rib:constraints="@cityLabel"
              />
              <Label rib:id="stateLabel"
                text="State:"
                labelFor="@stateField"
                horizontalAlignment="RIGHT"
              />
              <ComboBox
                rib:id="stateField"
                rib:constraints="@stateLabel"
              >
                ['AL', 'AR', 'CA', 'CO', 'MA', 'MI', 'TX', 'UT']
              </ComboBox>
              <Label rib:id="zipLabel"
                text="Zip/Postal Code:"
                labelFor="@zipField"
                horizontalAlignment="RIGHT"
              />
              <TextField rib:id="zipField"
                columns="10"
                rib:constraints="@zipLabel"
              />
            </Panel>
            </rib:arguments>
          </addTab>
          <addTab>
            <rib:arguments>
            <String>"Table"</String>
            <Panel rib:id="tablePanel"
              rib:constraints="CENTER"
             layout="java.awt.BorderLayout"
             >
              <ScrollPane rib:constraints="CENTER">
              swing.JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, \
              swing.JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS 
              <viewportView>
                <rib:arguments>
                <Table rib:id="dataTable">
                  [['Barry', 'A.', 'Feigenbaum'],
                  ['John', 'Q.', 'Public'],
                  ['John', '', 'Smith']
                  ], \
                  ['First', 'MI', 'Last']
                </Table>
                </rib:arguments>
              </viewportView>
              </ScrollPane>
            </Panel>
            </rib:arguments>
          </addTab>
          <addTab>
            <rib:arguments>
            <String>"Tree"</String>
            <Panel rib:id="treePanel"
              rib:constraints="CENTER"
              layout="java.awt.BorderLayout"
            >
              <ScrollPane rib:constraints="CENTER">
              swing.JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, \
              swing.JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS 
              <viewportView>
                <rib:arguments>
                <Tree rib:id="tree"
                  rib:constraints="CENTER"
                  showsRootHandles="true"
                >
                  <model mod="{defTreeModel}"/>
                </Tree>
                </rib:arguments>
              </viewportView>
              </ScrollPane>
            </Panel>
            </rib:arguments>
           </addTab>
          </TabbedPane>
          <Panel rib:id="buttonPanel" rib:constraints="SOUTH" 
                 layout="java.awt.BorderLayout">
            <Box rib:constraints="SOUTH">
            swing.BoxLayout.X_AXIS
              <horizontalGlue/>
              <Button rib:id="clearButton" text="Clear">
                <mnemonic>'r'</mnemonic>
                <addActionListener>
                   nameField.text = ""
                   addrField.text = ""
                   cityField.text = ""
                   stateField.selectedIndex = 0
                </addActionListener>
              </Button>
              <horizontalStrut>16</horizontalStrut>
              <Button rib:id="exitButton" text="Exit">
                <mnemonic>'x'</mnemonic>
                <addActionListener>
                   from javax.swing \
                   import JOptionPane
                   confirm = \
                   JOptionPane.showConfirmDialog(
                     mainFrame, 
                     "Confirm Exit",
                     "Confirm Exit Dialog",
                     JOptionPane.YES_NO_OPTION
                   )
                   if eq(confirm,
                   JOptionPane.YES_OPTION):
                     lang.System.exit(0)
                </addActionListener>
              </Button>
              <horizontalGlue/>
            </Box>
          </Panel>
        </Panel>
      </getContentPane>
    </Frame>
  </rib:components>
</rib:gui>

通过 <model> 标签在第 4 个 <addTab> 标签中设置 JTree(请参见 图 2D)的模型。注意,解释该值是因为它包括在大括号中。这可能是因为整个 JTree 的内容是在 <rib:scripts> 标签中定义的(特别是在 <rib:scripts> 标签的 <rib:script> 子标签引用的 tree.py 文件中)。





回页首


体系结构和脚本语言

<rib:gui> 标签的 rib:architecturerib:scriptlang 属性包含 RIB 的两项强大功能。RIB 最吸引人的功能之一就是它支持用于构建 GUI 的体系结构。简而言之,该体系结构是用来创建和提供层次结构(如 GUI)的体系结构。目前,RIB 支持 AWT/Swing 和 SWT 体系结构。您可以通过实现 com.ibm.wac.rgb.engine.arch.Architecture 接口来添加其他 GUI 体系结构。

RIB 支持 JavaScript/Rhino、Jython 和 JRuby 脚本语言。RIB 使用开放源代码 Apache Jakarta Bean Scripting Framework (BSF) 来解释脚本代码,从而使向 RIB 添加脚本语言支持来获得 BSF 支持的语言变得简单一些。通过编写扩展 com.ibm.wac.rgb.interp.CodeInterpreter 的类并覆盖适当的方法,您可以添加 BSF 脚本语言。





回页首


使用 RIB 验证

RIB 的重要且很有用的一项功能是,根据用户指定的约束集来验证GUI。为了验证 GUI 组件的特定属性(或它们之间的关系)符合预先指定的参数,您可以实现 com.ibm.wac.rgb.validate.Validator 接口。验证机制为您提供了一个评估和测试工具,以加速 GUI 的开发和测试。

可以使用 RIB 验证 GUI 是通过脚本还是通过其他方式(如定制代码)构造的。如果验证出 GUI 不是通过 RIB 创建的 ,那么应该使用 com.ibm.wac.rgb.validate.ValidationEngine 类。

验证文档

使用 RIB 实现任何验证过程的基础是一个辅助 XML 文档,它指定了将检查对象的类型,以及验证这些对象所依据的约束。XML 验证文档在 RIB 中遵循相同的惯例并使用许多相同语言作为 GUI 描述文档。例如,清单 3 中的验证文档为 java.util.List 指定了约束:


清单 3. 指定验证约束
<?xml version="1.0"?>
<rib:validate>
  <rib:components>
    <java.util.List>
      <size rib:eq="5">
      <get rib:index="0">
      <get rib:index="4" rib:instanceof="java.lang.String">
    </java.util.List>
  <rib:components>
</rib:validate>

这个简单(但非典型)的示例要求 RIB 验证列表有 5 个元素,第一个元素不能为空,最后一个元素则是一个 String 对象。

可以指定其他关系,并且可以使用属性 rib:minrib:max 指定一个范围来进行测试这些关系,或者使用 rib:regexp 属性对其进行测试,该属性采用了一个常规表达式作为对正测试的属性的文本进行匹配所依据的值。

验证文档的整体结构与描述 GUI 的脚本的结构非常相似。通过在指定特定组件的类的元素中包含指定该组件的 ID 的 rib:id 属性,您可以验证该组件的属性。

验证 GUI 可访问性

与 RIB 一起打包的 JavaGuiValidator 的一个子类是 com.ibm.wac.rgb.validate.AccessibilityJavaGuiValidator ,它使用实现 javax.accessibility.Accessible 接口的组件对 GUI 进行验证。这包括所有 Swing 组件(即作为 javax.swing.JComponent 子类的那些组件)。

与 RIB 包一起提供的 com/ibm/wac/rgb/validate/swing_accessibility.xml 验证文档包含一些约束,如果想使用辅助性技术成功提供 GUI,则必须拥有一些更基本的组件属性并保持组件之间的关系,而验证文档中包含的约束就是用于这些属性和关系的。

从命令行运行 RIB 时,RIB 在构造和提供 GUI 之后调用验证机制。RIB 会生成消息,当某一组件无法满足您在验证文档中为其设置的约束时,这些消息会发出声明。例如,我们采用了以下摘录脚本,该脚本包含帮助辅助性技术提供文本字段的属性:

<TextField rib:id="nameField" columns="20"
  rib:constraints="@nameLabel" focusAccelerator="n">
  <acName name="name input field"/>
  <toolTipText tip="Enter your full name"/>
  <acRel>
    AccessibleRelation(AccessibleRelation.LABELED_BY,nameLabel)
  </acRel>
</TextField>

然后,删除可访问性特性(以故意引起验证错误和警告):

<TextField rib:id="nameField" columns="20"
  rib:constraints="@nameLabel">
</TextField>

如果现在运行该应用程序,那么默认可访问性验证器将生成以下输出:

level.INFO: Beginning validation...
level.WARNING: [nameField] No value returned for property/method toolTipText
level.ERROR: [nameField] All text components should hold the LABELED_BY 
  relation to a javax.swing.JLabel
level.WARNING: [nameField] The first text component of any container can be 
  given a focus accelerator via setFocusAccelerator to aid in navigation
level.INFO: Validation complete.

验证器指出了失败的严重性,然后是用方括号将正被验证的组件的名称(本例中是对象 ID)括起来。验证器指出了还没有设置组件的工具提示文本,该文本字段并没有保持与其他所有组件的 LABELED_BY 关系,而且,作为组件组中的第一个组件,它没有一个指定的聚焦加速器。可从 IBM Accessibility Center(请参阅 参考资料)获得有关这些属性以及辅助性技术如何使用它们的更多信息。

如果将脚本还原成原始格式,那么验证器将生成以下输出:

level.INFO: Beginning validation...
level.INFO: Validation complete.

您不会收到任何错误或警告,这正是您所期望的。





回页首


结束语

RIB 提供了一个功能强大的应用程序,以快速创建、测试、调试和评估 Java GUI。我们已经知道 RIB 的一些功能,其中包括支持许多脚本语言和各种体系结构的功能,这些应该能够证明 RIB 是使用 Java 语言开发和部署基于 GUI 的应用程序的一个非常有用的工具。它还可以提高生成辅助技术可以有效提供的应用程序的能力。我们鼓励您下载 RIB(请参阅 参考资料)尝试一下。与下载资料在一起的是一些样例程序(其中包括使用 AWT 和 SWT 的程序),以及用户安装和使用指令的指导。






回页首


下载

名字大小下载方法
j-ribsamples.zipHTTP
关于下载方法的信息


参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.

  • 单击本文顶部或底部的 Code图标下载本文的代码示例。除了两个 GUI 构建脚本之外,您还可以看到指定用于确定 Java Swing GUI 可访问性标准的 XML 文档。

  • 从 IBM alphaWorks 下载 IBM Reflexive User Interface Builder(RIB) 的最新版本。

  • 有关 java.lang.Class 和包 java.lang.reflect 类的详细信息,请参阅 Reflection API 中 Java Tutorial的页面。

  • Introduction to Jython, Part 1: Java programming made easier”( developerWorks,2004 年 4 月)和“ Introduction to Jython, Part 2: Programming essentials”( developerWorks,2004 年 4 月)为您介绍了 Jython 脚本语言,并为您提供了丰富的知识,以便您开始开发自己的基于 Jython 的应用程序。

  • Jython 主页是 Jython 下载和文档的正式来源。

  • alt.lang.jre: Get to know Jython”( developerWorks,2004 年 7 月)是介绍 Java Runtime Environment 的替代语言的系列文章的第一篇文章。

  • IBM Accessibility Center 的 IBM Guidelines for Writing Accessible Applications Using 100% Pure Java提供了关于帮助辅助技术提供文本字段的属性的信息。

  • 增强图形用户界面的可访问性”( developerWorks,2003 年 7 月)显示了如何修改 J2SE 的 Metal 外观,以满足用户的特殊需要,如为视力有缺陷的人提供高对比度和大字体。

  • 为可访问性编码”( developerWorks,2002 年 10 月)说明了如何使用基于 JFC/Swing 的可访问性工具包,以最小的努力获得最高级别的可访问性。

  • Get started with the AUIML Toolkit”( developerWorks,2004 年 7 月)从 alphaWorks 引进了一个快速开发工具,它可以帮助开发人员编写 GUI,从而将其作为 Swing 应用程序运行,或者在 Web 上运行它,无需进行任何更改。

  • developerWorksJava 技术专区 中可以找到数百种有关 Java 技术的资源。

  • 请访问 Developer Bookstore,获取技术书籍的完整列表,其中包括数百本 Java 相关主题的图书。

  • 是否对无需通常的高成本入口点(entry point )或短期评估许可证的 IBM 测试产品感兴趣? developerWorks Subscription为 WebSphere、DB2、Lotus、Rational 和 Tivoli 产品提供了低成本的 12 个月单用户许可证,包括基于 Eclipse 的 WebSphere Studio IDE,用于开发、测试、评估和展示您的应用程序。


作者简介

Author photo

Barry Feigenbaum 博士是 IBM Worldwide Accessibility Center 的一名成员,他协助 IBM 让有残疾的人可以访问其产品。Feigenbaum 博士出版了许多书和文章,有多项专利,还曾在 JavaOne 等行业会议上发表过演讲。他是得克萨斯大学奥斯汀分校计算机科学的兼职助理教授。您可以通过 feigenba@us.ibm.com与 Feigenbaum 博士联系。


Author photo

Michael Squillace 博士是 IBM Worldwide Accessibility Center 的成员,在那里,他开发使人们可以访问 IBM 软件产品的工具。他拥有哲学博士学位,目前正在得克萨斯大学学习,主修计算机科学专业。他还是职业钢琴家,而且,他是一位盲人。您可以通过 masquill@us.ibm.com与 Squillace 博士联系。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款