内容


借助传统方法使用高级 GWT 控件

集成 JavaScript 库来增强 Google Web Toolkit 小部件

Comments

各种开源 Ajax 框架和商业工具箱都试图通过隐藏复杂性以及隐藏底层的 JavaScript 和文档对象模型(Document Object Model,DOM)操作的负担来简化 Ajax 开发。这样做的确可以减少开发时间,但如果想利用它们来实现高级功能,常常需要具备一定的 JavaScript 专业知识和技能,这些技能和知识往往不易习得。所幸的是 Google 现已提出一种创新技术,将 Ajax 应用程序开发带到了一个全新的阶段。GWT 是用途广泛的 API 和工具集,可以让您在 Java 代码中创建动态 Web 应用程序。GWT 是通过代码生成实现上述目标的:GWT 编译器可从客户端 Java 代码生成跨浏览器兼容的 JavaScript。

不幸的是,Google 提供的标准 UI 控件(小部件)的功能集十分有限,而且在大多数情况下尚不能满足现代企业级应用程序的需求。虽然您可以利用一组定制属性扩展基本的 GWT 控件,但这种方法常常会需要很多的开发工作。所幸的是,现在已经有了一种简便的方法。本文通过一个示例应用程序(参见 下载)展示了如何将流行的 ActiveWidgets JavaScript 网格组件集成到 GWT 应用程序中,从而通过较为简单的编码获得高级功能。

动态 Web UI 控件

JavaScript Web UI 控件旨在通过在浏览器内部动态生成 HTML 来消除服务器往返。服务器会将动态组件的数据结构和 JavaScript 代码添加到页面内容中,而非发送静态 HTML。在页面加载阶段,浏览器运行这些脚本来创建活动组件,配置这些组件,然后将每个组件生成的 HTML 字符串插入到页面的适当位置。就这一点而言,JavaScript 组件代码与此页面上的 HTML 片段是相联系的。组件可管理用户交互、根据数据更改升级 HTML 并提供一个 API 来处理其内容、行为和可视样式。

用 Java 代码编写并在 JavaScript 内编译的 GWT 控件与此类似。这就让 GWT 控件能够与商业控件及 JavaScript 库完全兼容。JavaScript 库则更为成熟,并能提供更全面的跨浏览器高级功能,比如智能滚动、列大小重置、排序、动态加载和针对大型数据集的分页。GWT 的开放架构让您能够将商业控件及传统 JavaScript 库与新应用程序相集成,这样一来,工作的重点就是解决企业面临的挑战和问题,而不是重头开始重复创建。

自动资源注入

本文随带的示例 GWT 应用程序(参见 下载 部分)使用了一个简单的 GWT 特性,即 自动资源注入。项目模块包括对外部 JavaScript 和层叠样式表 (CSS) 文件的引用,这样,当模块自身在加载时,外部 JavaScript 和层叠样式表 (CSS) 文件也可以自动加载。清单 1 显示了在示例应用程序的模块定义 XML 文件 (GridDemo.gwt.xml)中的声明,该声明可实现此目的:

清单 1. GWT 模块定义
<module>
  <inherits name='com.google.gwt.user.User'/>
  <entry-point class='com.mycompany.project.client.GridDemo'/>
  
  <source path="client"/>

  <stylesheet src="runtime/styles/xp/aw.css"/>
  <script src="runtime/lib/aw.js"> 
    <![CDATA[ 
     if ($wnd.AW.Grid.Control.create) 
       return true; 
     else 
       return false; 
   ]]> 
  </script>
</module>

JavaScript 文件和样式表的注入是一种极为方便的手段,可用来自动关联外部文件和 GWT 模块。所有注入了的资源均应放在 com\mycompany\project\public 目录中,该目录定义了 GWT Web 服务器的根。

AW.Grid.Control.create 是脚本就绪函数,它在网格控件脚本将被初始化时,返回 true。就绪函数的作用是要清楚地断定脚本完全被加载以便 GWT 代码可以使用此脚本,并要确保引用标识符可用。在清单 1 所示的示例中,AW.Grid.Control.create 函数的存在表明脚本已就绪。此函数是控件库的一部分并可作为外部资源,它必须以 $wnd 为前缀。

救难的 JavaScript Native Interface

JavaScript 网格控件本身的集成基于 JavaScript Native Interface (JSNI) 的 GWT 实现,JSNI 功能强大,您可以在 Java 源代码中加入 JavaScript 代码。清单 2 展示了 JavaScript 网格控件是如何通过 JSNI 进行实例化和初始化的:

清单 2. 网格控件的初始化(片段)
native JavaScriptObject init(JavaScriptObject myColumns,JavaScriptObject myData)/*-{
  try{
    $wnd.mygrid = new $wnd.AW.UI.Grid;
    $wnd.mygrid.setSize(750, 350);

    // provide cells and headers text
    $wnd.mygrid.setCellText(myData);
    $wnd.mygrid.setHeaderText(myColumns);

    // set number of rows/columns
    $wnd.mygrid.setRowCount(myData.length);
    $wnd.mygrid.setColumnCount(myColumns.length);
    ...
    $doc.getElementById('mygrid').innerHTML = $wnd.mygrid;

    return $wnd.mygrid;
  }
  catch(e){
    $wnd.alert(e.description);
  }

  return null;

}-*/;

JSNI 可以视为内联汇编代码的 Web 对等物,可以用来:

  • 直接在 JavaScript 内实现 Java 代码。
  • 为现有的 JavaScript 包装上类型安全的 Java 方法签名。
  • 从 JavaScript 调用 Java 代码,反之亦然。
  • 跨 Java/JavaScript 界线抛出异常。
  • 从 JavaScript 读写 Java 字段。
  • 使用宿主模式调试 Java 源代码(通过 Java 调试器)和 JavaScript(通过脚本调试器)。

GWT 开发文档提示说必须慎重使用 JSNI ,因为不能保证浏览器的可移植性,而且编译代码的优化是有限制的。至于商业控件,您可以依赖供应商的承诺来支持多个浏览器类型和部署平台。

当从 JSNI 访问浏览器窗口和文档对象时,必须将其分别作为 $wnd$doc 加以引用。而所编译的脚本则在嵌套的框架中运行,$wnd$doc 会自动初始化以正确指代宿主页面的窗口和文档。

示例代码展示了一种很好的技巧,即使用 JSNI 来将一维或二维的 Java 字符串数组转换成 JavaScript 数组,以便于用数据填充数组,如清单 3 所示:

清单 3. Java 到 JavaScript 数组的转换
public static JavaScriptObject arrayConvert(String[] array) { 
    JavaScriptObject result = newJSArray(array.length); 
    for (int i = 0; i<array.length; i++) { 
      arraySet(result, i, array[i]); 
    } 
    return result; 
} 

private static native JavaScriptObject newJSArray(int length) /*-{ 
    return new Array(length);  
}-*/; 

public static native int arrayLength(JavaScriptObject array) /*-{ 
    return array.length; 
}-*/; 

public static native String arrayGetObject(JavaScriptObject array, int index) /*-{ 
    return array[index]; 
}-*/; 

public static native void arraySet(JavaScriptObject array,int index,String value) /*-{ 
    array[index] = value; 
}-*/;

处理事件

在 JavaScript 中定义的事件处理程序可与用 Java 代码实现的方法相关联。清单 4 展示了网格控件的 onRowClicked 事件是如何调用 onRowSelect Java 函数的:

清单 4. 从 JavaScript 调用 Java 函数
public void onRowSelect(String index){
  GWT.log("Row #" + index + "selected", null);
}

native JavaScriptObject init(JavaScriptObject myColumns, JavaScriptObject myData)/*-{
  var widget = this;
  
  try{
      ... 
      // set click action handler
      $wnd.mygrid.onRowClicked = function(event, index){

widget.@com.mycompany.project.client.GwtGrid::onRowSelect(Ljava/lang/String;)(index);
      };

      ...
 }
 catch(e){
  $wnd.alert(e.description);
 }
}-*/;

您必须使用如下表示法在 JavaScript 中引用 Java 方法: instance-expr.@class-name::method-name(param-signature)(arguments)

  • instance-expr 在调用实例方法时必须使用,在调用静态方法时必须去掉。
  • class-name 是类的完全限定名,方法在此类(或其子类)中声明。
  • param-signature 是这里指定的内部 Java 方法签名,但无此方法返回类型的尾随签名(因为不需要选择重载)。
  • arguments 是实际要传递给被调方法的参数列表。

使用 JavaScript 控件 API

清单 2 所示,实现 Java 类的 init 方法返回 JavaScriptObject,它代表一个网格实例。您可以将其作为一个类属性加以存储并随后在代码中引用,以调用 JavaScript 控件 API 方法。清单 5 展示了 Delete 按钮单击事件在 Java 代码中是如何处理的:

清单 5. JavaScript 方法调用
protected JavaScriptObject grid = null;

...

public void onload(){
  if(grid == null){
    grid = init(...);
  }
}

public void onDeleteButtonClick(){
  delete(grid, getCurrentRow(grid));
}


public native void delete(JavaScriptObject obj, int index) /*-{

 try{
    obj.deleteRow(index);
 }
 catch(e){
    $wnd.alert(e.description);
 }

}-*/;	

public native int getCurrentRow(JavaScriptObject obj) /*-{
 
  try{
    return obj.getCurrentRow();
  }
  catch(e){
    $wnd.alert(e.description);
  }

  return -1;

}-*/;

在清单 5 中,Java 事件处理程序调用 JavaScript 控件 API 方法。这充分展示了与控件实例交互的能力。您可以为所有的 JavaScript 控件 API 定义类似的 Java 包装程序,并使其对 GWT 可用。

运行时配置

演示的应用程序要求必须在系统上安装 GWT SDK。为了运行实例代码,只需将下载的带有源代码和编译脚本的归档文件解压缩到 GWT SDK 发布目录的示例文件夹。如果可以运行 GWT 示例,就可以利用如下命令(在 Windows 平台上)启动示例应用程序:

your_local_path\gwt-windows-1.3.3\samples\GridDemo\GridDemo-shell.cmd

示例 GWT 应用程序演示了集成的商业 JavaScript 网格组件,该组件由 ActiveWidgets 开发,可在该公司的站点下载评估版(参阅 参考资料)。图 1 给出了对应的结果:

图 1. 示例 GWT 应用程序中的高级网格控件
使用中的网格控件
使用中的网格控件

示例应用程序归档文件包含了除 GWT 外的所有必需的库。我已经用所有的主流 GWT 发布版本测试过(包括 1.3 版本)。

结束语

本文通过一个示例展示了如何将传统的 JavaScript 网格控件与 GWT 应用程序进行集成。您可以将同样的方法应用于市面上可用的其他高级控件。除了商业 JavaScript 控件外,您还可以参考 Yahoo! User Interface Library,内含大量开源 JavaScript 实用工具和控件,可用来借助 DOM 脚本、DHTML 和 Ajax(参阅 参考资料)构建高交互性的 Web 应用程序。


下载资源


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, Web development
ArticleID=226768
ArticleTitle=借助传统方法使用高级 GWT 控件
publish-date=05282007