面向 Java 开发人员的 Ajax: 探索 Google Web Toolkit

介: 最近发布的 Google Web Toolkit (GWT) 是一组全面的 API 和工具,它支持用户几乎完全使用 Java™ 代码来创建动态 Web 应用程序。Philip McCarthy 回到了他广受欢迎的面向 Java 开发人员的 Ajax 系列,向您展示 GWT 能做什么。

GWT(请参阅 参考资料)采用了一种不寻常的方式进行 Web 应用程序开发。它没有采用客户端和服务器端代码库的普通隔离,而是提供了一个 Java API,该 API 允许创建基于组件的 GUI,然后编译它们,从而在用户的 Web 浏览器上显示它们。与一般的 Web 应用程序开发体验相比,使用 GWT 更接近于使用 Swing 或 SWT 进行开发,它还试图将 HTTP 协议和 HTML DOM 模型抽象出去。实际上,应用程序最终几乎总是会呈现在 Web 浏览器中。

GWT 是通过代码生成来实现这些功能的,它利用其编译器从客户端 Java 代码生成 JavaScript。GWT 支持 java.lang 和 java.util包的子集,还支持 GWT 自身提供的 API。编译后的 GWT 应用程序由 HTML、XML 和 JavaScript 片段组成。但是,这些片段很难区分,所以最好把编译后的应用程序当成是黑盒子 —— Java 字节码的 GWT 等价物。

在这篇文章中,我将创建一个简单的 GWT 应用程序,用该程序从远程 Web API 获得天气报告,并在浏览器中显示它。在整个过程中,我将简要介绍尽可能多的 GWT 功能,还将提到一些可能遇到的潜在问题。

从简单的开始

清单 1 显示了可以用 GWT 制作的最简单的应用程序的 Java 源代码:


清单 1. 最简单的 GWT 示例
public class Simple implements EntryPoint {

   public void onModuleLoad() {
     final Button button = new Button("Say 'Hello'");

     button.addClickListener(new ClickListener() {
        public void onClick(Widget sender) {
        Window.alert("Hello World!");
        }
     });

     RootPanel.get().add(button);
   }
}

这个代码看起来非常像使用 Swing、AWT 或 SWT 编写的 GUI 代码。不出所料,清单 1 创建了一个按钮,在单击此按钮时会显示消息 “Hello World!”。该按钮被添加到 RootPanel,这是一个环绕 HTML 页面主体的 GWT 包装对象。图 1 显示了应用程序在 GWT Shell 中运行时的情况。GWT Shell 是一个包含在 GWT SDK 中的调试宿主环境(debugging hosting environment),与一个简单的浏览器组合在一起。

构建 Weather Reporter 应用程序

我将用 GWT 创建一个简单的 Weather Reporter 应用程序。该应用程序的 GUI 向用户显示了一个用于输入 ZIP 代码的输入框,还显示了一个使用摄氏温度还是华氏温度来表示温度的选项。当用户单击 Submit 按钮时,该应用程序用 Yahoo! 的免费天气 API 获得所选定地区的 RSS 格式的报告。然后提取这个文档的 HTML 部分,并将它显示给用户。

GWT 应用程序被打包成模块,并且必须符合特定的结构。名为 module-name.gwt.xml 的配置文件定义了充当应用程序入口点的类,并指明是否要从其他 GWT 模块继承资源。在应用程序的源包结构中,必须将配置文件放在与 client 包和 public 目录相同的级别上,所有客户端 Java 代码都在 client 包中,而 public 目录包含项目的 Web 资源,比如图片、CSS 和 HTML。最后,public 目录中必须包含一个 HTML 文件,该文件中必须有一个包含模块的限定名称的 meta 标记。GWT 的运行时 JavaScript 库使用这个文件来初始化应用程序。

在指定了入口点类的情况下,GWT 的 applicationCreator 会替您生成这个基本结构。所以可以将调用 
applicationCreator developerworks.gwt.weather.client.Weather 生成一个项目框架作为创建 Weather Reporter 应用程序的起点。在该应用程序的源代码下载中包含的 Ant 构建文件中,有一些有用的目标(target),可使用它们让 GWT 项目符合这个结构。(请参阅 下载)。

开发基本的 GUI

首先,我将开发应用程序的用户界面小部件(widget)的基本布局,且不添加其他任何行为。Widget 类是可以呈现在 GWT UI 中的几乎所有类的超类。Widget 总是包含在 Panel中,Panel 本身也是 Widget,所以可以被嵌套。不同类型的面板提供了不同的布局行为。所以,GWT Panel 扮演的角色与 AWT/Swing 中的 Layout 或 XUL 中的 Box 类似。

所有小部件和面板最终都要附加到包含它们的 Web 页面上。如 清单 1 所示,可以直接把它们附加到 RootPanel 上。或者,可以用RootPanel 获得对使用 ID 或类名标识的 HTML 元素的引用。在这个示例中,我将使用两个独立的 HTML DIV 元素,它们的名称分别是input-container 和 output-container。第一个元素包含 Weather Reporter 应用程序的 UI 控件,第二个元素显示天气报告本身。

清单 2 显示了设置基本布局所需的代码;它应当是自解释的。HTML 小部件只是 HTML 标记的容器,来自 Yahoo! 天气种子(weather feed)的 HTML 输出将显示在这里。这些代码都位于 Weather 类的 onModuleLoad() 方法中,这个方法由 EntryPoint 接口提供。在将包含天气模块的 Web 页面装入客户机的 Web 浏览器时,将调用这个方法。


清单 2. Weather Reporter 应用程序的布局代码
public void onModuleLoad() {

   HorizontalPanel inputPanel = new HorizontalPanel();

   // Align child widgets along middle of panel
   inputPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);

   Label lbl = new Label("5-digit zipcode: ");
   inputPanel.add(lbl);

   TextBox txBox = new TextBox();
   txBox.setVisibleLength(20);

   inputPanel.add(txBox);

   // Create radio button group to select units in C or F
   Panel radioPanel = new VerticalPanel();

   RadioButton ucRadio = new RadioButton("units", "Celsius");
   RadioButton ufRadio = new RadioButton("units", "Fahrenheit");

   // Default to Celsius
   ucRadio.setChecked(true);

   radioPanel.add(ucRadio);
   radioPanel.add(ufRadio);

   // Add radio buttons panel to inputs
   inputPanel.add(radioPanel);

   // Create Submit button
   Button btn = new Button("Submit");    

   // Add button to inputs, aligned to bottom
   inputPanel.add(btn);
   inputPanel.setCellVerticalAlignment(btn,
      HasVerticalAlignment.ALIGN_BOTTOM);

   RootPanel.get("input-container").add(inputPanel);

   // Create widget for HTML output
   HTML weatherHtml = new HTML();

   RootPanel.get("output-container").add(weatherHtml);
}

用 CSS 添加样式

呈现的 Web 页面看起来很傻,所以它将从 CSS 样式规则中汲取一些优点。可以用两种方式为 GWT 应用程序添加样式。首先,默认情况下,每个小部件都有一个 CSS 类名,其形式为 project-widget。例如,gwt-Button 和 gwt-RadioButton 是两个核心 GWT 小部件类名。面板通常被实现为一堆嵌套式表格,所以没有默认的类名。

每个小部件类型一个类名(classname-per-widget-type)的默认方法使得在整个应用程序中一致地设置小部件样式变得非常容易。当然,普通的 CSS 选择器规则也可以应用,所以可以根据小部件的上下文,用选择器规则在同一小部件上应用不同的样式。要得到更多的灵活性,则可以调用小部件的 setStyleName() 和 addStyleName() 方法,临时替换和增加小部件的默认类名。

清单 3 组合了这些方法,把样式应用到 Weather Reporter 应用程序的输入面板上。通过对 inputPanel.setStyleName("weather-input-panel"); 的调用,在 Weather.java 中创建了 weather-input-panel 类名。


清单 3. 将 CSS 样式应用到 Weather Reporter 应用程序的输入面板
/* Style the input panel itself */
.weather-input-panel {
   background-color: #AACCFF;
   border: 2px solid #3366CC;
   font-weight: bold;
}

/* Apply padding to every element within the input panel */
.weather-input-panel * {
   padding: 3px;
}

/* Override the default button style */
.gwt-Button {
   background-color: #3366CC;
   color: white;
   font-weight: bold;
   border: 1px solid #AACCFF;
}

/* Apply a hover effect to the button */
.gwt-Button:hover {
   background-color: #FF0084;
}

添加客户端行为

现在应用程序的基本布局和样式已经就绪,我将开始实现一些客户端行为。可以用熟悉的侦听器模式在 GWT 中执行事件处理。GWT 为鼠标事件、键盘事件、修改事件等提供了 Listener 接口,还提供了几个适配器和助手类,以获得更多方便。

一般情况下使用 Swing 程序员熟悉的内部类形式来添加事件侦听器。但是,所有 GWT Listener 方法的第一个参数都是事件的发送者,通常是用户刚刚与之交互的小部件。这意味着可以把同一个 Listener 实例附加到所需的多个小部件上;可以用 sender 参数确定是哪个小部件触发了事件。

清单 4 显示了 Weather Reporter 应用程序中实现的两个事件侦听器。click 句柄被添加到了 Submit 按钮上,keyhandler 被添加到了TextBox 上。不管是单击 Submit 按钮,还是在 TextBox 拥有焦点时按下回车键,都会导致相关的句柄调用私有的validateAndSubmit() 方法。在添加到清单 4 的代码中之后,txBox 和 ucRadio 已经成为 Weather 类的实例变量,所以可以从验证方法访问它们。


清单 4. 添加客户端行为
// Create Submit button, with click listener inner class attached
Button btn = new Button("Submit", new ClickListener() {

   public void onClick(Widget sender) {
      validateAndSubmit();
   }
});

// For usability, also submit data when the user hits Enter 
// when the textbox has focus
txBox.addKeyboardListener(new KeyboardListenerAdapter(){

   public void onKeyPress(Widget sender, char keyCode, int modifiers) {

      // Check for Enter key
      if ((keyCode == 13) && (modifiers == 0)) {
         validateAndSubmit();
      }        
   }      
});    

清单 5 显示了 validateAndSubmit() 方法的实现。该实现非常简单,由封装验证逻辑的 ZipCodeValidator 类完成。如果用户没有输入正确的 5 位数字的 ZIP 代码,那么 validateAndSubmit() 将在警告框中显示错误消息,如果这种情况出现在 GWT 中,则会调用Window.alert()。如果 ZIP 代码正确,那么它将与用户对摄氏或华氏温度单位的选择一起被传递给 fetchWeatherHtml() 方法,这个方法稍后再介绍。


清单 5. validateAndSubmit 逻辑
private void validateAndSubmit() {

   // Trim whitespace from input
   String zip = txBox.getText().trim();

   if (!zipValidator.isValid(zip)) {
     Window.alert("Zip-code must have 5 digits");
     return;
   }

   // Disable the TextBox
   txBox.setEnabled(false);

   // Get choice of celsius/fahrenheit
   boolean celsius = ucRadio.isChecked();
   fetchWeatherHtml(zip, celsius);
}

用 GWT Shell 进行客户端调试

在这里我要岔开一会,提一下 GWT Shell,它拥有允许在 Java IDE 中调试客户端代码的 JVM 挂钩。您可以与 Web UI 进行交互,分步调试表示客户端执行的相应 JavaScript 代码的 Java 代码。这是一项很重要的功能,因为在客户端上调试所生成的 JavaScript 基本上是不可能的。

可以很容易地配置一个 Eclipse 调试任务,从而通过 com.google.gwt.dev.GWTShell 类启动 GWT Shell。图 4 显示了按下 Submit 按钮后,在 validateAndSubmit() 方法的断点处暂停的 Eclipse:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值