[转载]Reflexive User Interface Builder 简介

Reflexive User Interface Builder 简介


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 脚本

xmlns:rib="com.ibm.wac.rgb"
xmlns="swing"
rib:scriptlang="jython"
rib:architecture="swing"
>

import javax.accessibility.AccessibleRelation as AccRelation


rib:name="BorderLayout"
rib:value="java.awt.BorderLayout"
/>
rib:name="acName"
rib:value="!getAccessibleContext!setAccessibleName"
/>


300, 150
224, 224, 255


size="@screenDim"
title="RGB -- Sample 1 fddlkjlkdlkdflkd"
background="@bkgdColor"
>




nameField.requestFocus()


layout="%BorderLayout"
>

swing.BoxLayout.X_AXIS



swing.BoxLayout.X_AXIS











本例从 RIB 根 标签开始,该标签可以有 4 个子元素:

别名

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


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

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


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

对象

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


300, 150
224, 224, 255

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


300, 150




回页首


创建一个简单的 GUI

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

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

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

   title="RGB -- Sample 1" background="@bkgdColor">

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

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


:

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

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

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

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

       layout="%BorderLayout">

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

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

swing.BoxLayout.X_AXIS


标签内有多个组件:

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

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

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

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

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




回页首


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

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


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


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

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


lang.System.exit(0)

与 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 文档

xmlns:rib="com.ibm.wac.rgb"
rib:scriptlang="jython"
rib:architecture="swing"
>

rib:name="initScript"
rib:file="examples/swing/tree.py"
rib:exec="true"
/>

def getCtrlKeyStroke (keyCode):
return KeyStroke.getKeyStroke(
keyCode, InputEvent.CTRL_MASK )



rib:name="PVLayout"
rib:value="com.ibm.wac.rgb.swing.PromptedValueLayout"
/>


400, 300
224, 224, 255
255, 0, 255

[10, 10, 10], [5, 5, 5]



size="@screenDim"
title="TabbedPane Sample"
background="@bkgdColor"
jMenuBar="{swing.JMenuBar()}"
>


lang.System.exit(0)



"File"
KeyEvent.VK_F
accelerator="{getCtrlKeyStroke(KeyEvent.VK_N)}"
text="new"
/>
accelerator="{getCtrlKeyStroke(KeyEvent.VK_O)}"
text="Open"
/>
accelerator="{getCtrlKeyStroke(KeyEvent.VK_S)}"
text="Save"
/>

"Edit"
KeyEvent.VK_E
accelerator="{getCtrlKeyStroke(KeyEvent.VK_X)}"
text="Cut"
/>
accelerator="{getCtrlKeyStroke(KeyEvent.VK_C)}"
text="Copy"
/>
accelerator="{getCtrlKeyStroke(KeyEvent.VK_V)}"
text="Paste"
/>



layout="java.awt.BorderLayout"
>



"Label"




"Form"
rib:constraints="NORTH"
layout="@pvLayout"
>





"Table"
rib:constraints="CENTER"
layout="java.awt.BorderLayout"
>

swing.JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
swing.JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS



[['Barry', 'A.', 'Feigenbaum'],
['John', 'Q.', 'Public'],
['John', '', 'Smith']
],
['First', 'MI', 'Last']









"Tree"
rib:constraints="CENTER"
layout="java.awt.BorderLayout"
>

swing.JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
swing.JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS


rib:constraints="CENTER"
showsRootHandles="true"
>









layout="java.awt.BorderLayout">

swing.BoxLayout.X_AXIS


16









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




回页首


体系结构和脚本语言

标签的 rib:architecture 和 rib: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. 指定验证约束










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

可以指定其他关系,并且可以使用属性 rib:min 和 rib: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 会生成消息,当某一组件无法满足您在验证文档中为其设置的约束时,这些消息会发出声明。例如,我们采用了以下摘录脚本,该脚本包含帮助辅助性技术提供文本字段的属性:

  rib:constraints="@nameLabel" focusAccelerator="n">



AccessibleRelation(AccessibleRelation.LABELED_BY,nameLabel)


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

  rib:constraints="@nameLabel">

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

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 的程序),以及用户安装和使用指令的指导。

请使用浏览器的分享功能分享到微信等