使用 Google Web Toolkit(GWT)和 NetBeans 构建 AJAX 应用程序

Sang Shin, sang.shin@sun.com, Sun Microsystems, www.javapassion.cn



本动手实验室将指导您使用 Google Web Toolkit(GWT)开发基于 AJAX 的 Web 应用程序。本文档旨在帮助您尽快入门。在本动手实验室中,您将使用 GWT 的以下特性:
  1. 如何使用 GWT NetBeans IDE 插件构建一个基于 GWT 的应用程序
  2. 如何使用 NetBeans IDE 构建并运行来自 Google 的 GWT 所随附的示例应用程序
  3. 如何使用 GWT 部件
  4. 如何创建并使用自定义的部件
  5. 如何执行 RPC 操作
  6. 如何以 JSON 的形式从服务器获取数据
  7. 如果将 CSS 风格附加给部件
  8. 如何让 Java 和本地的 Javascript 代码互相调用。


预计时间:300 分钟

前提条件 Prerequisites

本动手实验室假定您拥有以下技术的基本知识或者具备相关编程经验:

所需软件 Software Needed

在开始之前,您需要在您的计算机上安装以下软件。GWT Netbeans IDE 插件已经包含在动手实验室压缩文件中。GWT NetBeans IDE 插件此时仍处于初期,但它提供了够好的模板来使用 NetBeans IDE 构建并运行 GWT 应用程序。越来越多的功能正加到该插件中来。当新插件可用时,本动手实验室将随之更新。


针对本实验室您所能使用的操作系统平台


Google Web 工具包 1.3.3 版(至 2007 年 5 月 16 号的最新版)仅支持以下操作系统平台:

更新日志


将要做的事情(由本实验室作者计划)


实验室练习


在本实验室中,一旦您下载并安装了上述提到的软件,就不再需要因特网链接。


练习 0:安装与配置

在本练习中,您将把 GWT NetBeans 插件安装到 NetBeans IDE 中。

    1. 安装 GWT NetBeans 插件到 NetBeans IDE 中

(0.1) 安装 GWT NetBeans 插件到 NetBeans IDE 6.0 中






练习 1:构建并运行一个简单的 HelloWorld 应用程序

在本练习中,您将使用 NetBeans 构建一个简单的 HelloWorld GWT 应用程序。您将把 Google Web 工具包安装路径配置到 NetBeans IDE 中,这只需操作一次。

  1. 创建一个 GWT Web 应用程序项目
  2. 添加另一个按钮到 GWT 应用程序中

(1.1) 创建一个 GWT Web 应用程序项目

0.如果没有启动 NetBeans IDE 则启动它。0.如果您还没有启动 NetBeans IDE 请启动它。
1.创建一个 GWT NetBeans 项目。


图 1.10:创建一个新 Web 应用程序项目


图 1.11:指定项目名称

2. 选择 Google Web Toolkit 作为要使用的框架并设定 GWT 安装目录。

当您第一次创建 GWT 项目时,NetBeans IDE 询问 Google Web Toolkit(GWT) 包所下载并解压的 的位置(如上面软件需求部分中所述)。

图 1.11.1:GWT 配置


图 1.12:GWTApplication 项目

3.生成并运行应用程序。


图 1.13.第一个页面


图 1.14:显示结果

返回顶部练习

(1.2) 添加另一个按钮到 GWT 应用程序中


1. GWTApplication->源包->org.yournamehere.client 下的 MainEntryPoint.java 文件以下面代码 1.20 所示。请特别注意粗体部分。

/*
* MainEntryPoint.java
*
* Created on May 18, 2007, 5:31 PM
*
* To change this template, choose Tools | Template Manager
* and open the template in the editor.
*/

package org.yournamehere.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

/**
*
* @author sang
*/
public class MainEntryPoint implements EntryPoint {

/** Creates a new instance of MainEntryPoint */
public MainEntryPoint() {
}

/**
* The entry point method, called automatically by loading a module
* that declares an implementing class as an entry-point
*/
public void onModuleLoad() {
final Label label = new Label("Hello, GWT!!!");
final Button button = new Button("Click me!");

button.addClickListener(new ClickListener(){
public void onClick(Widget w) {
label.setVisible(!label.isVisible());
}
});

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

}
代码 1.20:包含有一个按钮的 Main.java

2. 修改 MainEntryPoint.java 文件以下面代码 1.21 所示 来添加第二个按钮。需要添加的代码段突出显示为蓝色粗体字。

package org.yournamehere.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

/**
*
* @author sang
*/
public class MainEntryPoint implements EntryPoint {

/** Creates a new instance of MainEntryPoint */
public MainEntryPoint() {
}

/**
* The entry point method, called automatically by loading a module
* that declares an implementing class as an entry-point
*/
public void onModuleLoad() {
final Label label = new Label("Hello, GWT!!!");
final Button button = new Button("Click me!");

button.addClickListener(new ClickListener(){
public void onClick(Widget w) {
label.setVisible(!label.isVisible());
}
});

Button button2 = new Button("I am the 2nd button. Click me!");

button2.addClickListener(new ClickListener(){
public void onClick(Widget w) {
Window.alert("Life is worth living.. with Passion!");
}
});

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

}
代码 1.21:添加了第二个按钮

3.构建并运行项目。

图 1.22: 出现了第二个按钮


图 1.23: 来自第二个按钮的事件处理程序的 alert 消息

返回顶部练习

(1.3) 调试



















结束语

在本练习中,您学习了如何使用 NetBeans 构建并运行一个 HelloWorld GWT 应用程序。

return to the top



练习 2:构建并运行 GWT 示例应用程序 “Hello”和“Kitchen Sink”,并添加 CSS 风格

在本练习中,您将使用 NetBeans IDE 构建两个示例应用程序 —— “Hello”以及 “Kitchen Sink”。两个应用程序都随附在 GWT 包中。它们同使用 GWT 包随附的命令行 shell 命令运行和生成的程序是同样的应用程序。您必须首先完成练习 1,因而 NetBeans IDE 中 GWT 的位置被正确地配置。


(2.1) 构建并运行 “Hello” 示例应用程序

在本步骤中,您将使用 NetBeans 构建并运行 “Hello”示例应用程序。在动手实验室的压缩文件中,已提供了该应用程序,是名为 GWTHello 的“可供生成”的 NetBeans 项目。此应用程序同练习 1 中您构建的 GWTApplication 运行起来完全一样。

0.如果您还没有启动 NetBeans IDE 请启动它。
1.打开 GWTHello NetBeans 项目。


图 2.10: 打开 GWTHello NetBeans 项目

3.构建并运行 GWTHello NetBeans 项目。

图 2.12: 在浏览器中打开应用程序

4. 添加另一个按钮到 GWTHello 应用程序。
/*
* Copyright 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.sample.hello.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

public class Hello implements EntryPoint {

public void onModuleLoad() {
Button b = new Button("Click me", new ClickListener() {
public void onClick(Widget sender) {
Window.alert("Hello, AJAX");
}
});

RootPanel.get().add(b);

// Create a new button along with event listener.
// The event listener has listener method called onClick().

Button c = new Button("Click me 2", new ClickListener() {
public void onClick(Widget sender) {
Window.alert("Hello, AJAX2");
}
});

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

}
代码 2.15:添加另一个按钮

5. 生成并运行项目


图 2.16: 以宿主模式运行的经过修改的 GWTHello 应用程序。

5. [针对您的练习] 

返回顶部练习


(2.2) 使用 NetBeans 构建并运行 “Kitchen Sink” 示例应用程序 —— 修改 Buttons.java

在本步骤中,您将使用 NetBeans 构建并运行“Kitchen Sink”示例应用程序,然后修改Buttons.java 以添加您自己的按钮。在动手实验室的压缩文件中,已提供了该应用程序,是名为 GWTKitchenSink 的“可供生成”的 NetBeans 项目

0.如果您还没有启动 NetBeans IDE 请启动它。
1.打开 GWTKitchenSink NetBeans 项目。

2.如果您遇到引用问题先解决引用(如上面所述)。

3.生成并运行 GWTKitchenSink 项目。


图 2.22: GWTKitchenSink 页面

4. 选中Buttons中左起的每一个部件。使用一下它们。(如下图 2.23 所示)


图 2.23: 按钮

5.试用以下:
6.对于 Frames,单击 Back Forward 按钮,并查看它们是否如预期一样运行。(如下图 2.24 所示)


图 2.24: 试用 Back 和 Forward 按钮

返回顶部练习

(2.3) 修改 Buttons.java


1.修改 GWTKitchenSink->com.google.gwt.sample.kitchensink.client 下的 Buttons.java 以下面代码 2.30 所示。添加粗体注释 是用来帮助您的理解,您在实际的代码中看不到它们。需要添加的新代码段突出显示为 蓝色粗体 字。新代码将添加名为 “Life”的具有其自已的菜单项的菜单栏。

/*
* Copyright 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.sample.kitchensink.client;

import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
* Demonstrates the various button widgets.
*/
public class Buttons extends Sink {

public static SinkInfo init() {
return new SinkInfo("Buttons",
"GWT supports all the myriad types of buttons that exist in HTML. "
+ "Here are a few for your viewing pleasure.") {
public Sink createInstance() {
return new Buttons();
}
};
}

// Create button and checkbox objects
private Button disabledButton = new Button("Disabled Button");
private CheckBox disabledCheck = new CheckBox("Disabled Check");
private Button normalButton = new Button("Normal Button");
private Button myOwnButton = new Button("My Own Button");
private CheckBox normalCheck = new CheckBox("Normal Check");

// Create a vertical panel
private VerticalPanel panel = new VerticalPanel();

// Create radio buttons
private RadioButton radio0 = new RadioButton("group0", "Choice 0");
private RadioButton radio1 = new RadioButton("group0", "Choice 1");
private RadioButton radio2 = new RadioButton("group0", "Choice 2 (Disabled)");
private RadioButton radio3 = new RadioButton("group0", "Choice 3");
private RadioButton radio4 = new RadioButton("group0", "Choice 4");

// Constructor of the Buttons class
public Buttons() {
HorizontalPanel hp;

// Create a horizontal panel object and add buttons to it.
// Also add the horizontal panel object to the vertical panel.
panel.add(hp = new HorizontalPanel());
hp.setSpacing(8);
hp.add(normalButton);
hp.add(disabledButton);
hp.add(myOwnButton);

// Create a horizontal panel object and add checkboxes to it.
// Also add the horizontal panel object to the vertical panel.
panel.add(hp = new HorizontalPanel());
hp.setSpacing(8);
hp.add(normalCheck);
hp.add(disabledCheck);

// Create a horizontal panel object and add radion buttons to it.
// Also add the horizontal panel object to the vertical panel.
panel.add(hp = new HorizontalPanel());
hp.setSpacing(8);
hp.add(radio0);
hp.add(radio1);
hp.add(radio2);
hp.add(radio3);
hp.add(radio4);

// Set the disabled flags to widget objects
disabledButton.setEnabled(false);
disabledCheck.setEnabled(false);
radio2.setEnabled(false);

panel.setSpacing(8);
setWidget(panel);
}

public void onShow() {
}
}
代码 2.30:经过修改的 Buttons.java

2.生成并运行项目。


图 2.31: 添加您自己的按钮

3.添加另一个面板到 GWTKitchenSink 应用程序中。
/*
* Copyright 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.sample.kitchensink.client;

import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
* Demonstrates the various button widgets.
*/
public class Buttons extends Sink {

public static SinkInfo init() {
return new SinkInfo("Buttons",
"GWT supports all the myriad types of buttons that exist in HTML. "
+ "Here are a few for your viewing pleasure.") {
public Sink createInstance() {
return new Buttons();
}
};
}

// Create button and checkbox objects
private Button disabledButton = new Button("Disabled Button");
private CheckBox disabledCheck = new CheckBox("Disabled Check");
private Button normalButton = new Button("Normal Button");
private Button myOwnButton = new Button("My Own Button");
private CheckBox normalCheck = new CheckBox("Normal Check");

// Create a vertical panel
private VerticalPanel panel = new VerticalPanel();

// Create radio buttons
private RadioButton radio0 = new RadioButton("group0", "Choice 0");
private RadioButton radio1 = new RadioButton("group0", "Choice 1");
private RadioButton radio2 = new RadioButton("group0", "Choice 2 (Disabled)");
private RadioButton radio3 = new RadioButton("group0", "Choice 3");
private RadioButton radio4 = new RadioButton("group0", "Choice 4");

// Constructor of the Buttons class
public Buttons() {
HorizontalPanel hp;

// Create a horizontal panel object and add buttons to it.
// Also add the horizontal panel object to the vertical panel.
panel.add(hp = new HorizontalPanel());
hp.setSpacing(8);
hp.add(normalButton);
hp.add(disabledButton);
hp.add(myOwnButton);

// Create a horizontal panel object and add checkboxes to it.
// Also add the horizontal panel object to the vertical panel.
panel.add(hp = new HorizontalPanel());
hp.setSpacing(8);
hp.add(normalCheck);
hp.add(disabledCheck);

// Create a horizontal panel object and add radion buttons to it.
// Also add the horizontal panel object to the vertical panel.
panel.add(hp = new HorizontalPanel());
hp.setSpacing(8);
hp.add(radio0);
hp.add(radio1);
hp.add(radio2);
hp.add(radio3);
hp.add(radio4);

// Add my own panel
panel.add(hp = new HorizontalPanel());

hp.setSpacing(8);
hp.add(normalButton);
hp.add(normalCheck);
hp.add(radio0);

// Set the disabled flags to widget objects
disabledButton.setEnabled(false);
disabledCheck.setEnabled(false);
radio2.setEnabled(false);

panel.setSpacing(8);
setWidget(panel);
}

public void onShow() {
}
}
代码 2.32:添加另一个面板

4. 生成并运行项目。

图 2.33: 运行修改过的应用程序的结果

返回顶部练习


(2.4) 修改 Menus.java


1.修改 GWTKitchenSink->com.google.gwt.sample.kitchensink.client 下的Menus.java 以下面代码 2.40 所示。添加 粗体注释是用来帮助您的理解,您在实际的代码中看不到它们。需要添加的新代码段突出显示为蓝色粗体 字。新代码将添加另一个名为“Life”具有其自己的菜单项的菜单栏。

/*
* Copyright 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.sample.kitchensink.client;

import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.MenuBar;
import com.google.gwt.user.client.ui.MenuItem;

/**
* Demonstrates {@link com.google.gwt.user.client.ui.MenuBar} and
* {@link com.google.gwt.user.client.ui.MenuItem}.
*/
public class Menus extends Sink implements Command {

public static SinkInfo init() {
return new SinkInfo("Menus",
"The GWT <code>MenuBar</code> class makes it easy to build menus, "
+ "including cascading sub-menus.") {
public Sink createInstance() {
return new Menus();
}
};
}

// Create empty master MenuBar object
private MenuBar menu = new MenuBar();

public Menus() {

// Create a sub-sub-menu bar and add items to the sub-sub-menu bar
MenuBar subMenu = new MenuBar(true);
subMenu.addItem("<code>Code</code>", true, this);
subMenu.addItem("<strike>Strikethrough</strike>", true, this);
subMenu.addItem("<u>Underlined</u>", true, this);

// Create a new sub-menu bar and add items to it
MenuBar menu0 = new MenuBar(true);
menu0.addItem("<b>Bold</b>", true, this);
menu0.addItem("<i>Italicized</i>", true, this);
menu0.addItem("More &#187;", true, subMenu);

// Create a new sub-menu bar and add items to it
MenuBar menu1 = new MenuBar(true);
menu1.addItem("<font color='#FF0000'><b>Apple</b></font>", true, this);
menu1.addItem("<font color='#FFFF00'><b>Banana</b></font>", true, this);
menu1.addItem("<font color='#FFFFFF'><b>Coconut</b></font>", true, this);
menu1.addItem("<font color='#8B4513'><b>Donut</b></font>", true, this);

// Create a new sub-menu bar and add items to it
MenuBar menu2 = new MenuBar(true);
menu2.addItem("Bling", this);
menu2.addItem("Ginormous", this);
menu2.addItem("<code>w00t!</code>", true, this);

// Create a new sub-menu bar and add items to it
MenuBar menu3 = new MenuBar(true);
menu3.addItem("Life is good!", this);
menu3.addItem("Life is OK!", this);
menu3.addItem("Life sucks!", this);

// Add sub-menu bar's to the master menu bar
menu.addItem(new MenuItem("Style", menu0));
menu.addItem(new MenuItem("Fruit", menu1));
menu.addItem(new MenuItem("Term", menu2));
menu.addItem(new MenuItem("Life", menu3));

menu.setWidth("100%");

setWidget(menu);
}

public void execute() {
Window.alert("Thank you for selecting a menu item.");
}

public void onShow() {
}
}

代码 2.40:添加您自己的菜单栏

2. 生成并运行项目。

图 2.41: 显示了新的菜单栏


返回顶部练习


(2.5) 修改 Trees.java


1.修改 GWTKitchenSink->com.google.gwt.sample.kitchensink.client 下的 Trees.java 以下面代码 2.50 所示。添加 粗体注释 是用来帮助您的理解。您在实际代码中看不到它们。需要添加的新代码段突出显示为 蓝色粗体 字。

private static Proto[] fProto = new Proto[]{
new Proto("Beethoven", new Proto[]{
new Proto("Concertos", new Proto[]{
new Proto("No. 1 - C"), new Proto("No. 2 - B-Flat Major"),
new Proto("No. 3 - C Minor"), new Proto("No. 4 - G Major"),
new Proto("No. 5 - E-Flat Major"),}),
new Proto("Quartets", new Proto[]{
new Proto("Six String Quartets"), new Proto("Three String Quartets"),
new Proto("Grosse Fugue for String Quartets"),}),
new Proto("Sonatas", new Proto[]{
new Proto("Sonata in A Minor"), new Proto("Sonata in F Major"),}),
new Proto("Symphonies", new Proto[]{
new Proto("No. 1 - C Major"), new Proto("No. 2 - D Major"),
new Proto("No. 3 - E-Flat Major"), new Proto("No. 4 - B-Flat Major"),
new Proto("No. 5 - C Minor"), new Proto("No. 6 - F Major"),
new Proto("No. 7 - A Major"), new Proto("No. 8 - F Major"),
new Proto("No. 9 - D Minor"),}),}),
new Proto("Sang Shin", new Proto[]{
new Proto("Rock and Roll", new Proto[]{
new Proto("Love me tender"), new Proto("Let it Be"),
new Proto("Hey Jude"),}),
new Proto("Country music", new Proto[]{
new Proto("Lost Highway"), new Proto("Six Days on the Road"),
new Proto("Wide Open Road"),}),
new Proto("Tango", new Proto[]{
new Proto("Jealousy"), new Proto("Tango of Roses"),}),
new Proto("Hip Hop", new Proto[]{
new Proto("No. 1 - C Major"), new Proto("No. 2 - D Major"),
new Proto("No. 9 - D Minor"),}),}),
new Proto("Brahms", new Proto[]{
new Proto("Concertos", new Proto[]{
new Proto("Violin Concerto"), new Proto("Double Concerto - A Minor"),
new Proto("Piano Concerto No. 1 - D Minor"),
new Proto("Piano Concerto No. 2 - B-Flat Major"),}),
new Proto("Quartets", new Proto[]{
new Proto("Piano Quartet No. 1 - G Minor"),
new Proto("Piano Quartet No. 2 - A Major"),
new Proto("Piano Quartet No. 3 - C Minor"),
new Proto("String Quartet No. 3 - B-Flat Minor"),}),
new Proto("Sonatas", new Proto[]{
new Proto("Two Sonatas for Clarinet - F Minor"),
new Proto("Two Sonatas for Clarinet - E-Flat Major"),}),
new Proto("Symphonies", new Proto[]{
new Proto("No. 1 - C Minor"), new Proto("No. 2 - D Minor"),
new Proto("No. 3 - F Major"), new Proto("No. 4 - E Minor"),}),}),
new Proto("Mozart", new Proto[]{new Proto("Concertos", new Proto[]{
new Proto("Piano Concerto No. 12"), new Proto("Piano Concerto No. 17"),
new Proto("Clarinet Concerto"), new Proto("Violin Concerto No. 5"),
new Proto("Violin Concerto No. 4"),}),}),};

代码 2.50:Modified Trees.java

2. 生成并运行项目。

图 2.51: 新添加的 Tree 节点

返回顶部练习

(2.6) 改变 CSS 风格


在本步骤中,您将学习如何创建并使用 CSS 风格。

0.打开 GWTKitchenSink->源包->com.google.gwt.sample.kitchensink.public 目录下的KitchenSink.css



1.在 KitchenSink.css 文件中创建一种新风格 .ks-List .ks-SinkItem-selected-myown 下面代码 2.60 所示。您可能想在 .ks-List .ks-SinkItem-selected之后添加它。位置开始于行号 203 行左右。

.ks-List .ks-SinkItem-selected {
background-color: #C3D9FF;
}

/* -------------- This is my own ------ */

.ks-List .ks-SinkItem-selected-myown {
background-color: #EEFFEE;
font-weight: bold;
font-style: italic;
}
代码 2.60:修改 .ks-List .ks-SinkItem-selected 的属性

2.修改GWTKitchenSink->源包->com.google.gwt.sample.kitchensink.client下的 SinkList.java 文件中的 setSinkSelection 方法(从行号 59 开始)以下面代码 2.61 所示来使用新创建的风格。需要修改的代码段突出显示为 蓝色粗体 字。

public void setSinkSelection(String name) {
if (selectedSink != -1)
list.getWidget(selectedSink).removeStyleName("ks-SinkItem-selected-myown");

for (int i = 0; i < sinks.size(); ++i) {
SinkInfo info = (SinkInfo) sinks.get(i);
if (info.getName().equals(name)) {
selectedSink = i;
list.getWidget(selectedSink).addStyleName("ks-SinkItem-selected-myown");
return;
}
}
}
代码 2.61:使用新风格

3.生成并运行项目

图 2.62: 使用不同风格

返回顶部练习

结束语


在本练习中,您使用 NetBeans 构建了两个 GWT 示例应用程序 “Hello”和“Kitchen Sink”。您已经学习了如何使用由 GWT 提供的各种部件,也学习了如何给一个部件设置风格。

返回顶部


练习 3:创建自定义部件


在本练习中,首先您将使用 Composite 类创建一个简单自定义的部件。 Composite 是一种能包装其他部件、并隐藏包装部件的方法的部件。当把 Composite 添加到面板中,其行为就跟其包装的部件添加到面板中一样。 当在一个单独的面板中,想创建一个在多个聚集的部件之外的单个部件是很有用的。您也将创建另一个从第一个自定义部件扩展而来的自定义部件。

(3.1) 创建一个新的 GWT 项目


0.如果您还没有启动 NetBeans IDE 请启动它。
1. 选择 文件->新建项目 (Ctrl+Shift+N)。出现了新建项目 对话框。
2.在 选择项目 窗格中, 选择 分类 下的Web , 项目下的 Web 应用程序 。单击 Next. 出现了新建 Web 应用程序对话框 。


GW
3.在 项目 字段中, 输入 GWTCompositeExample
4. 单击 下一步。(如下图 3.10 所示)


图 3.10: 创建一个名为 GWTComposteExample 的新项目

5.注意到出现了框架面板。
6.选择 Google Web Toolkit
7.在 GWT Module 字段中,输入 my.company.Main。(或者您就使用缺省值)
8. 单击 完成



9. 注意到GWTCompositeExample 项目结点出现了。

返回顶部练习


(3.2) 创建一个名为“OptionalTextBox”的自定义部件


本示例基于 Google Web Toolkit 网站中JavaDoc of Composite class 里的代码。请在继续本步骤之前阅读它。

1. 使用下面显示的代码 3.20 替换 IDE 生成的 GWTCompositeExample->源码->my.company.client 下的 MainEntryPoint.java

这些代码扩展 Composite 类创建一个名为 OptionalTextBox 的自定义部件。当您让 OptionalTextBox 部件可用时,它让您在文本字段中输入一些东西。当其不可用时,则无法输入。您能从下面代码 3.20 复制和粘贴代码。

package my.company.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

/**
*
* @author sang
*/
public class MainEntryPoint implements EntryPoint {

/** Creates a new instance of MainEntryPoint */
public MainEntryPoint() {
}

/**
* A composite of a TextBox and a CheckBox that optionally enables it.
*/
public static class OptionalTextBox extends Composite implements
ClickListener {

private TextBox textBox = new TextBox();
private CheckBox checkBox = new CheckBox();

/**
* Constructs an OptionalTextBox with the given caption on the check.
*
* @param caption the caption to be displayed with the check box
*/
public OptionalTextBox(String caption) {
// Place the check above the text box using a vertical panel.
VerticalPanel panel = new VerticalPanel();
panel.add(checkBox);
panel.add(textBox);

// Set the check box's caption, and check it by default.
checkBox.setText(caption);
checkBox.setChecked(true);
checkBox.addClickListener(this);

// All composites must call initWidget() in their constructors.
initWidget(panel);

// Give the overall composite a style name.
setStyleName("example-OptionalCheckBox");
}

public void onClick(Widget sender) {
if (sender == checkBox) {
// When the check box is clicked, update the text box's enabled state.
textBox.setEnabled(checkBox.isChecked());
}
}

/**
* Sets the caption associated with the check box.
*
* @param caption the check box's caption
*/
public void setCaption(String caption) {
// Note how we use the use composition of the contained widgets to provide
// only the methods that we want to.
checkBox.setText(caption);
}

/**
* Gets the caption associated with the check box.
*
* @return the check box's caption
*/
public String getCaption() {
return checkBox.getText();
}
}

public void onModuleLoad() {
// Create an optional text box and add it to the root panel.
OptionalTextBox otb = new OptionalTextBox("Check this to enable me");
RootPanel.get().add(otb);
}

}
代码 3.20:经过修改的 MainEntryPoint.java

返回顶部练习


(3.3) 生成并运行应用程序


1. 右键单击 GWTCompositeExample 项目节点并选择 运行
2. 单击 GWT page
3. 因为选中了 Check this to enable me 复选框,您能在文本字段中输入。输入如下图 3.30 所示的字符串。


图 3.30: OptionalTextBox 部件实践

4.现在不选中 Check this to enable me 复选框。 现在您将注意到您刚才输入的字符变灰了,并且您不能再输入任何字符。(如下图 3.31 所示)


图 3.31: OptionalTextBox 部件实践

返回顶部练习



结束语


在本练习中,您创建并使用两个自定义部件。您通过扩展 Composite 类创建了第一个自定义部件。您通过扩展第一个部件创建第二个自定义部件。


return to the top



练习 4:通过 RPC 调用远程服务


在本练习中,您将构建一个使用 RPC 以异步方式跟服务器通信的应用程序。这里有服务器和客户端的代码。 在您做此练习之前,您阅读一下 description of Remote Procedure Call feature of GWT(推荐但不是必须)。
  1. 创建一个新的 NetBeans 项目
  2. 使用 NetBeans GWT 插件的向导创建一段 RPC 示例代码
  3. 深入了解生成的代码
  4. 了解经过修改的 web.xml 文件
  5. 修改客户端代码 ManinEntryPoint.java 来调用远程服务
  6. 构建并运行应用程序
  7. 调试服务器端代码
  8. 调试客户端代码

(4.1) 创建一个新的 NetBeans 项目


1. Create GWTHelloRPC project.

图 4.10: 创建 GWTHelloRPC 项目

2. 选择 Google Web Toolkit 作为框架。

图 4.11: 选择 Google Web Toolkit 为框架

返回顶部练习

(4.2) 创建示例 RPC 代码


在本步骤中,您将使用 NetBeans GWT 插件的 RPC 向导创建一个 RPC 项目。

1. 右键单击 GWTHelloRPC 项目节点并选择 新建->文件/文件夹. (如下图 4.21 所示)


图 4.21: 新建文件/文件夹

2. 选择 分类 下的 Google Web Toolkit文件类型下的 GWT RPC Service 。(如下图 4.22 所示)


图 4.22: 创建 GWT RPC 服务

3. 在 Service Name 字段,仅保留缺省值 GWTService 或者输入您选择的服务名称。在本例中,我们将使用缺省的名称。 4. 选中 Create Usage Example Class 复选框。IDE 将生成示例 RPC 代码。
5. 单击 完成。 (如下图 4.23 所示)


图 4.23: 选中 Create Usage Example Class。

返回顶部练习

(4.3) 深入了解生成的代码


该向导生成以下源文件。在本步骤中,您将研究这些这些源文件。
本向导也修改了 web.xml 文件。

1. 打开 GWTService.java 并研究代码。 粗体注释是添加来帮助您的理解的。这些注释并没有出现在由 IDE 生成的实际代码中。(以下面代码 4.31 所示)

package my.company.client;
import com.google.gwt.user.client.rpc.RemoteService;

// Service interface, which has a single method called myMethod.
public interface GWTService extends RemoteService{
public String myMethod(String s);
}
代码 4.31:GWTService.java

2. 打开 GWTServiceAsync.java 并研究代码。 粗体注释是添加来帮助您的理解的。(以下面代码 4.32 所示)

package my.company.client;
import com.google.gwt.user.client.rpc.AsyncCallback;

// Asynchronous Service interface. Note that a AsynchCallBack
// object needs to be passed as a parameter.
public interface GWTServiceAsync {
public void myMethod(String s, AsyncCallback callback);
}
代码 4.32:GWTServiceAsync.java

3. 打开 GWTServiceImpl.java 并研究代码。粗体注释是添加来帮助您的理解的。(以下面代码 4.33 所示)

package my.company.server;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import my.company.client.GWTService;

// Service implementation, which provides the implementation of
// GWTService service interface.
public class GWTServiceImpl extends RemoteServiceServlet implements
GWTService {

public String myMethod(String s) {
// Do something interesting with 's' here on the server.
return "Server says: " + s;
}
}
代码 4.33:GWTServiceImpl.java

4. 打开 GWTServiceUsageExample.java 并研究代码。对于粗体注释要给予特别注意。 (以下面代码 4.34 所示)

package my.company.client;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;

public class GWTServiceUsageExample extends VerticalPanel {
private Label lblServerReply = new Label();
private TextBox txtUserInput = new TextBox();
private Button btnSend = new Button("Send to server");

public GWTServiceUsageExample() {
add(new Label("Input your text: "));
add(txtUserInput);
add(btnSend);
add(lblServerReply);

// Create an asynchronous callback to handle the result.
// The AynchCallBack object has to provide implementation
// of onSuccess() and onFailure() metods.
final AsyncCallback callback = new AsyncCallback() {
public void onSuccess(Object result) {
lblServerReply.setText((String)result);
}

public void onFailure(Throwable caught) {
lblServerReply.setText("Communication failed");
}
};

// Listen for the button clicks. When "Send to server"
// button is pressed, the RPC operation occurs.
btnSend.addClickListener(new ClickListener(){
public void onClick(Widget w) {
// Make remote call. Control flow will continue immediately and later
// 'callback' will be invoked when the RPC completes.
getService().myMethod(txtUserInput.getText(), callback);
}
});
}

public static GWTServiceAsync getService(){
// Create the client proxy. Note that although you are creating the
// service interface proper, you cast the result to the asynchronous
// version of
// the interface. The cast is always safe because the generated proxy
// implements the asynchronous interface automatically.
GWTServiceAsync service = (GWTServiceAsync) GWT.create(GWTService.class);
// Specify the URL at which our service implementation is running.
// Note that the target URL must reside on the same domain and port from
// which the host page was served.
//
ServiceDefTarget endpoint = (ServiceDefTarget) service;
String moduleRelativeURL = GWT.getModuleBaseURL() + "gwtservice";
endpoint.setServiceEntryPoint(moduleRelativeURL);
return service;
}
}
代码 4.34:GWTServiceUsageExample.java

返回顶部练习


(4.4) 了解 web.xml 文件


1. 打开 GWTHelloRPC->Configuration Files 下的 web.xml 并研究由 IDE 所做的更改。对于粗体字部分要给予特别注意。(以下面代码 4.40 所示)

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<servlet>
<servlet-name>GWTService</servlet-name>
<servlet-class>my.company.server.GWTServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>GWTService</servlet-name>
<url-pattern>/my.company.Main/gwtservice</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>
index.jsp
</welcome-file>
</welcome-file-list>
</web-app>
代码 4.40:经过修改的 web.xml

返回顶部练习


(4.5) 修改 MainEntryPoint.java


现在在本步骤中,您将修改 MainEntryPoint.java 以使您能测试 RPC 操作。

1. 修改 GWTHelloRPC->源包->my.company.client 下的 MainEntryPoint.java 以下面代码 4.51 所示。本修改将使用并显示 RootPanel 中的 GWTServiceUsageExample 对象,从而您能测试代码。

package my.company.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

/**
*
* @author sang
*/
public class MainEntryPoint implements EntryPoint {

/** Creates a new instance of MainEntryPoint */
public MainEntryPoint() {
}

/**
* The entry point method, called automatically by loading a module
* that declares an implementing class as an entry-point
*/
public void onModuleLoad() {
final Label label = new Label("Hello, GWT!!!");
final Button button = new Button("Click me!");

// The GWTServiceUsageExample is a VerticalPanel object, the constructor of
// which performs RPC operation with the server.
GWTServiceUsageExample rpcCallingPanel = new GWTServiceUsageExample();

button.addClickListener(new ClickListener(){
public void onClick(Widget w) {
label.setVisible(!label.isVisible());
}
});

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

// Add the GWTServiceUsageExample to the panel
// so that you can test it.
RootPanel.get().add(rpcCallingPanel);
}

}
代码 4.51:经过修改的 MainEntryPoint.java

返回顶部练习


(4.6) 生成并运行项目


1. 生成并运行项目。

图 4.60: 在输入文本框中输入值
注意:关于如何安装 Firebug 调试器的信息,请参阅 这里。 关于如何使用 Firebug 调试器的信息,请参阅 这里


图 4.61: 显示服务器返回的数据

返回顶部练习


(4.7) 调试服务器端代码


使用 GWT 的益处之一就是您能够使用 Java 技术对服务器和客户端代码执行所有的调试。在本步骤中,您将使用 NetBeans IDE 的调试工具调试服务器端代码。在下一步中,您将调试客户端代码。

1. 右键单击 GWTHelloRPC 项目节点并选择 设置为主项目。(如果您想调试一个 NetBeans 项目,您必须将其设置为主项目)

2. 如下图 4.70 所示在 GWTServiceImpl.java 中设置断点。这是确保服务器端逻辑如预期般运行。要设置一个断点,在您想要设置断点的那行的灰色竖栏上单击。


图 4.70: 设置断点

3. 选择菜单栏上的 运行 并选择 调试主项目 来启动调试。(如下图 4.71 所示) 应用程序以调试模式启动。


图 4.71: 调试主项目

4. 注意到出现了两个窗口 —— 一个标题为 Google Web Toolkit Development Shell,另一个标题为 JSP Page。 此时,应用程序以宿主模式运行(同 web 模式相对)。


图 4.72: Google Web Toolkit 开发 Shell


图 4.73: JSP 页面

5. 单击 GWT page 链接。
6. 在文本框中输入一些消息 —— 本例中是 Life is good! 。(如下图 4.74 所示)
7. 单击 Send to server 按钮。


图 4.74: 在调试模式中,发送数据到服务器

8. 现在您将命中断点。断点行的颜色变成淡绿色。如果没有出现调试窗口,选择菜单栏中的窗口,并选择 调试->本地变量。如下图 4.72 所示,您应该看到字符串当作参数传递。在本例中,“Life is good!”就是传递给 myMethod() 方法的字符串值。


图 4.72: 注意到命中断点及本地变量查看

9. 从菜单栏选择运行并且选择完成调试会话(或者按下 Shift+F5)
10. 单击该行的灰色栏来移除断点

返回顶部练习


(4.8) 调试客户端代码


1. 如下图 4.80 中所示,在 GWTServiceUsageExample.java 中设置断点。设置断点的行是行号为 41 的 lblServerReply.setText((String)result);


图 4.80: 在客户端代码中设置断点

2. 从菜单栏 运行 并选择 调试主项目 来启动调试。(如下图 4.71 所示) 应用程序以调试模式启动。
3. 注意到出现了两个窗口 —— 一个标题为 Google Web Toolkit Development Shell 另一个为 JSP Page。 此时,应用程序以宿主模式运行(同 web 模式相对)。
4. 单击 GWT page 链接。
5. 在文本字段中输入一些消息(Life is really good!
7. 单击 Send to server 按钮。
8. 您现在应该命中断点。断点选择颜色变为淡绿色。您应该看到服务器返回的字符串如下图 4.81 所示。在本例中,“Server says" Life is really good!"” 就是从服务器返回的字符串值。


图 4.81: 服务器返回值

9. 从菜单栏选择运行并且选择完成调试会话(或者按下 Shift+F5)
10. 单击该行的灰色栏来移除断点

返回顶部练习


结束语


在本练习中,您练习了使用 GWT 的 RPC 能力。


返回顶部



练习 5:构建并运行“Dynamic Table”示例应用程序

在本练习中,您将要使用 NetBeans IDE 构建并运行随附于 Google Web 工具包的“Dynamic Table”示例应用程序。此示例应用程序使用 GWT 的远程过程调用(RPC)功能。它也将创建并使用自定义部件。


(5.1) 构建并运行 “Dynamic Table” 示例应用程序

0.如果您还没有启动 NetBeans IDE 请启动它。
1. 打开 GWTDynaTable 项目。

2. 如果您遇到引用问题则解决引用(如 上面所述)。
3. 运行 GWTDynaTable 项目..


图 5.10:“Dynamic Table” 示例应用程序的运行结果


图 5.11: 重新显示方案

返回顶部练习

(5.2) 深入了解“Dynamic Table”示例应用程序 —— 代码的 RPC 部分


在本步骤中,您将探讨应用程序的 RPC 部分。首先,让我们看一下服务接口文件 —— SchoolCalendarService.java SchoolCalendarServicAsync.java。

1.双击 GWTDynaTable->源包->com.google.gwt.sample.dynatable.client 下的 SchoolCalendarService.java,在源编辑器中打开它。添加突出显示为粗体的注释是用来帮忙您的理解的。 (以下面代码 5.20 所示)

/*
* Copyright 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.sample.dynatable.client;

import com.google.gwt.user.client.rpc.RemoteService;

// Service interface. It has one method called getPeople.
public interface SchoolCalendarService extends RemoteService {

Person[] getPeople(int startIndex, int maxCount);

}
代码 5.20:SchoolCalendarService.java

2. 双击 GWTDynaTable->源包->com.google.gwt.sample.dynatable.client 下的 SchoolCalendarServiceAsync.java,在源编辑器中打开它。添加突出显示为粗体的注释是用来帮忙您的理解的。 (以下面代码 5.21 所示)

/*
* Copyright 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.sample.dynatable.client;

import com.google.gwt.user.client.rpc.AsyncCallback;

// Asynchronous service interface. It has AsyncCallback object as a parameter.
public interface SchoolCalendarServiceAsync {

void getPeople(int startIndex, int maxCount, AsyncCallback callback);

}
代码 5.21:SchoolCalendarServiceAsync.java

现在来看一下服务器端实现类。

3. 双击 GWTDynaTable->源包->com.google.gwt.sample.dynatable.server 下的 SchoolCalendarServiceImpl.java,在源编辑器中打开它。添加突出显示为粗体的注释是用来帮忙您的理解的。 (以下面代码 5.22 所示)

/*
* Copyright 2006 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.sample.dynatable.server;

import com.google.gwt.sample.dynatable.client.Person;
import com.google.gwt.sample.dynatable.client.Professor;
import com.google.gwt.sample.dynatable.client.Schedule;
import com.google.gwt.sample.dynatable.client.SchoolCalendarService;
import com.google.gwt.sample.dynatable.client.Student;
import com.google.gwt.sample.dynatable.client.TimeSlot;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

// This is service implementation on the server side. The service implementation
// has to extends "RemoteServiceServlet" class and implements service interface.
public class SchoolCalendarServiceImpl extends RemoteServiceServlet implements
SchoolCalendarService {

private static final String[] FIRST_NAMES = new String[]{
"Inman", "Sally", "Omar", "Teddy", "Jimmy", "Cathy", "Barney", "Fred",
"Eddie", "Carlos"};

private static final String[] LAST_NAMES = new String[]{
"Smith", "Jones", "Epps", "Gibbs", "Webber", "Blum", "Mendez", "Crutcher",
"Needler", "Wilson", "Chase", "Edelstein"};

private static final String[] SUBJECTS = new String[]{
"Chemistry", "Phrenology", "Geometry", "Underwater Basket Weaving",
"Basketball", "Computer Science", "Statistics", "Materials Engineering",
"English Literature", "Geology"};

private static final Person[] NO_PEOPLE = new Person[0];

public SchoolCalendarServiceImpl() {
generateRandomPeople();
}

// This method implements a method defined in the service interface.
// This method returns an array of Person objects.
public Person[] getPeople(int startIndex, int maxCount) {
int peopleCount = people.size();

int start = startIndex;
if (start >= peopleCount) {
return NO_PEOPLE;
}

int end = Math.min(startIndex + maxCount, peopleCount);
if (start == end) {
return NO_PEOPLE;
}

int resultCount = end - start;
Person[] results = new Person[resultCount];
for (int from = start, to = 0; to < resultCount; ++from, ++to) {
results[to] = (Person) people.get(from);
}

return results;
}

private void generateRandomPeople() {
for (int i = 0; i < MAX_PEOPLE; ++i) {
Person person = generateRandomPerson();
people.add(person);
}
}

private Person generateRandomPerson() {
// 1 out of every so many people is a prof.
//
if (rnd.nextInt(STUDENTS_PER_PROF) == 1) {
return generateRandomProfessor();
} else {
return generateRandomStudent();
}
}

private Person generateRandomProfessor() {
Professor prof = new Professor();

String firstName = pickRandomString(FIRST_NAMES);
String lastName = pickRandomString(LAST_NAMES);
prof.setName("Dr. " + firstName + " " + lastName);

String subject = pickRandomString(SUBJECTS);
prof.setDescription("Professor of " + subject);

generateRandomSchedule(prof.getTeachingSchedule());

return prof;
}

private void generateRandomSchedule(Schedule sched) {
int range = MAX_SCHED_ENTRIES - MIN_SCHED_ENTRIES + 1;
int howMany = MIN_SCHED_ENTRIES + rnd.nextInt(range);

TimeSlot[] timeSlots = new TimeSlot[howMany];

for (int i = 0; i < howMany; ++i) {
int startHrs = 8 + rnd.nextInt(9); // 8 am - 5 pm
int startMins = 15 * rnd.nextInt(4); // on the hour or some quarter
int dayOfWeek = 1 + rnd.nextInt(5); // Mon - Fri

int absStartMins = 60 * startHrs + startMins; // convert to minutes
int absStopMins = absStartMins + CLASS_LENGTH_MINS;

timeSlots[i] = new TimeSlot(dayOfWeek, absStartMins, absStopMins);
}

Arrays.sort(timeSlots);

for (int i = 0; i < howMany; ++i) {
sched.addTimeSlot(timeSlots[i]);
}
}

private Person generateRandomStudent() {
Student student = new Student();

String firstName = pickRandomString(FIRST_NAMES);
String lastName = pickRandomString(LAST_NAMES);
student.setName(firstName + " " + lastName);

String subject = pickRandomString(SUBJECTS);
student.setDescription("Majoring in " + subject);

generateRandomSchedule(student.getClassSchedule());

return student;
}

private String pickRandomString(String[] a) {
int i = rnd.nextInt(a.length);
return a[i];
}

/**
* Write the serialized response out to stdout. This is a very unusual thing
* to do, but it allows us to create a static file version of the response
* without deploying a servlet.
*/
protected void onAfterResponseSerialized(String serializedResponse) {
System.out.println(serializedResponse);
}

private final List people = new ArrayList();
private final Random rnd = new Random(3);
private static final int CLASS_LENGTH_MINS = 50;
private static final int MAX_SCHED_ENTRIES = 5;
private static final int MIN_SCHED_ENTRIES = 1;
private static final int MAX_PEOPLE = 100;
private static final int STUDENTS_PER_PROF = 5;
}
代码 :SchoolCalendarServiceImpl.java

现在让我们来看看 SchoolCalendarService 服务的 getPeople() 方法是如何在客户端调用的。您将使用 NetBeans 的 在项目中查找 功能。

4. 选择顶层菜单的 编辑 并选择 在项目中查找。 (如下图 5.23 所示)


图 5.23: 在所有项目中查找

5. 出现了 在项目中查找 对话框。在 包含文本 字段中,输入 getPeople。 (如下图 5.24 所示)


图 5.24: 在所有项目中查找 getPeople

6. 包含 getPeople 字符串的所有代码都显示在 输出 窗口中。双击 SchoolCalendarWidget.java 中的那一个。 SchoolCalendarWidget.java 中调用 getPeople() 方法的位置就显示在源编辑器中了。 (如下图 5.25 所示)


图 5.25: 查找在何处调用了 getPeople() 方法

返回顶部练习

Summary


在本练习中,您已经构建了 “Dynamic Table” 示例应用程序,在其中,客户端通过 RPC 调用了远程服务上的一个方法。该应用程序也构建并使用了两个自定义部件。


return to the top



练习 6:构建并运行 “GWTJSON” 示例应用程序


在本练习中,您将构建并运行随附在 Google Web Toolkit 包中的 “JSON” 示例应用程序。本示例应用程序使用 JSON 作为浏览器和服务器之间的数据格式。

    1. 构建并运行 “GWTJSON” 示例应用程序

(6.1) 构建并运行 “GWTJSON” 示例应用程序

0.如果您还没有启动 NetBeans IDE 请启动它。
1. 打开 GWTJSON 项目。

2. 如果您遇到引用问题则解决引用(如 上面所述)。
3.生成并运行 GWTJSON 项目。


图 6.10:“JSON” 示例应用程序的运行结果

4. 注意到数据以 JSON 的形式从服务器端取到并以树的形式显示。(如下图 6.11 所示)


图 6.11: 数据以 JSON 的形式取到并以树的形式显示。


5. 通过按下 F12 键启用 FireBug 调试器。
6. 展开 Get http://localhost... 字符串来查看响应。
7. 观察响应中的 JSON 数据。(如下图 6.12 所示)


图 6.12: 以 Web 模式运行应用程序。

结束语


在本练习中,您已经构建并运行了 GWTJSON 示例应用程序。


return to the top



练习 7:构建并运行 “GWTMail” 示例应用程序


在本练习中,您将构建并运行随附在 Google Web Toolkit 包中的 “GWTMail” 示例应用程序。

    1. 构建并运行 “GWTMail” 示例应用程序

(7.1) 构建并运行 “GWTMail” 示例应用程序


0.如果您还没有启动 NetBeans IDE 请启动它。
1. 打开 GWTMail 项目

2.如果您遇到引用问题则解决引用(如 上面所述)。
3. 构建并运行 GWTMail 项目


图 7.10:“GWTMail” 示例应用程序的运行结果


图 7.11: Tasks


图 7.12: Contacts

结束语


在本练习中,您已经构建并运行“GWTMail”示例应用程序


返回顶部

练习 8:构建并运行 “GWTSimpleXML” 示例应用程序


在本练习中,您将构建并运行随附在 Google Web Toolkit 包中的 “GWTSimpleXML” 示例应用程序。

    1. 构建并运行 “GWTSimpleXML” 示例应用程序

(8.1) 构建并运行 “GWTSimpleXML” 示例应用程序


0.如果您还没有启动 NetBeans IDE 请启动它。
1. 打开 GWTSimpleXML 项目

2. 如果您遇到引用问题则解决引用(如 上面所述)。
3.生成并运行 GWTSimpleXML 项目


图 8.10: GWTSimpleXML 应用程序的运行结果

4. 按下 F12 键来显示通过 FireBug 调试器捕获的 XML 数据。(如下图 8.11 所示)


图 8.11: 捕获的 XML 数据

返回顶部


练习 9:构建并运行“HistoryExample” 示例应用程序


在本练习中,您将构建并运行“GWTHistoryExample”示例应用程序。该应用程序展示了如果同浏览器的历史栈发生交互。该代码来自 GWT 主站的 History class document

0.如果您还没有启动 NetBeans IDE 请启动它。
1. 打开 GWTHistoryExample 项目

2.如果您遇到引用问题则解决引用(如 上面所述)。
3.生成并运行 GWTHistoryExample project


图 9.10:GWTHistoryExample 示例应用程序的运行结果

3. 单击其中一个链接 —— 本例中是 link to bar —— 并且观察到 URL 也相应地发生了变化。


图 9.11: 单击 link to bar

返回顶部

练习 10:JavaScript 本地接口(JSNI)


在本练习中,您将学习如何从您的 Java 代码访问本地 JavaScript 代码或者反之。
  1. 从 Java 程序访问本地 JavaScript 代码
  2. 从本地 JavaScript 代码调用 Java 方法和访问 Java 字段

(10.1) 从 Java 程序访问本地 JavaScript 代码


在本步骤中,您将从您的 Java 应用程序中调用本地 JavaScript 代码。本步骤的解决方案是 samples 目录下的 GWTHelloNativeJavaScript。 请随意打开和运行解决方案项目来查看是如何工作的。

1. 通过复制 GWTHello 项目创建一个名的 GWTHelloNativeJavaScript 的新 NetBeans 项目。
2. 修改 GWTHelloNativeJavaScript->源包->com.google.gwt.sample.hello.client 下的 Hello.java 以下面代码 10.10 所示。(请确保您修改的是来自于新创建的 GWTHelloNativeJavaScript 项目中的 Hello.java 而不是 GWTHello 项目中的那一个。)

需要被注释掉的代码段突出显示为 红色粗体字而需要添加的代码突出显示为蓝色粗体 字。

package com.google.gwt.sample.hello.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

public class Hello implements EntryPoint {

public void onModuleLoad() {
Button b = new Button("Click me", new ClickListener() {
public void onClick(Widget sender) {
// Window.alert("Hello, AJAX");
myalert("Hello, AJAX using Native JavaScript");
}
});

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

//
// Add native JavaScript code.
//
public static native void myalert(String msg) /*-{
$wnd.alert(msg);
}-*/;

}
代码 10.10:调用本地 JavaScript 代码

3.生成并运行 GWTHelloNativeJavaScript 项目


图 10.11: 显示了来自本地 JavaScript 代码的消息

故障排除:如果您没有看到新的消息,而只是看到“Hello, AJAX”消息。有可能您修改了 GWTHello 项目中的 Hello.java 而不是 GWTHelloNativeJavaScript 中的。

(10.2) 从本地 JavaScript 调用 Java 方法和访问 Java 字段


在本步骤中,您将从本地 JavaScript 代码调用 Java 方法。您将从本地 JavaScript 代码访问 Java 程序的实例和静态字段。本步骤中的解决方案在 samples 目录的 GWTHelloCallJavaFromJavaScript 下。 请随意打开并运行该解决方案项目以查看是如何工作的。

1. 通过复制 GWTHelloNativeJavaScript 项目创建一个名为 GWTHelloCallJavaFromJavaScript 的 NetBeans 项目。



2. 修改 GWTHelloCallJavaFromJacaScript->源包 ->com.google.gwt.sample.hello.client 下的 Hello.java 以下面代码 10.20 所示。(请确保您修改来自于新创建的 GWTHelloCallJavaFromJavaScript 项目的 Hello.java 而不是 GWTHelloNativeJavaScript 项目的 GWTHello 。)



需要被注释掉的代码段突出显示为 红色粗体字而需要添加的代码突出显示为蓝色粗体 字。

package com.google.gwt.sample.hello.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;

public class Hello implements EntryPoint {

public void onModuleLoad() {
Button b = new Button("Click me", new ClickListener() {
public void onClick(Widget sender) {
// Window.alert("Hello, AJAX");
// myalert("Hello, AJAX using Native JavaScript");
bar(new Hello(), "Message sent to the bar native method from Java method");
}
});

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

// Add native JavaScript code.
public static native void myalert(String msg) /*-{
$wnd.alert(msg);
}-*/;

// Instance and static fields which will be accessed from JavaScript code
String myInstanceField = "Life is worth living with Passion!";
static String myStaticField;

// Instance method
void instanceFoo(String s) {
myalert(s);
}

// Static method
static void staticFoo(String s) {
myalert(s);
}

// Native method in which Java methods and fields are accessed from the
// JavaScript code.
//
public native void bar(Hello x, String s) /*-{
// Call instance method instanceFoo() on this
this.@com.google.gwt.sample.hello.client.Hello::instanceFoo(Ljava/lang/String;)("The 1st " + s);

// Call instance method instanceFoo() on x
x.@com.google.gwt.sample.hello.client.Hello::instanceFoo(Ljava/lang/String;)("The 2nd " + s);

// Call static method staticFoo()
@com.google.gwt.sample.hello.client.Hello::staticFoo(Ljava/lang/String;)("The 3rd " + s);

// Read instance field on this
var val = this.@com.google.gwt.sample.hello.client.Hello::myInstanceField;
@com.google.gwt.sample.hello.client.Hello::staticFoo(Ljava/lang/String;)("Instance field contains " + val);

// Write instance field on x and read it again
x.@com.google.gwt.sample.hello.client.Hello::myInstanceField = val + " Really!";
var val2 = x.@com.google.gwt.sample.hello.client.Hello::myInstanceField;
@com.google.gwt.sample.hello.client.Hello::staticFoo(Ljava/lang/String;)("Instance field contains " + val2);

// Write static field (no qualifier) and read it again
@com.google.gwt.sample.hello.client.Hello::myStaticField = "Message from static field";
var val3 = @com.google.gwt.sample.hello.client.Hello::myStaticField;
@com.google.gwt.sample.hello.client.Hello::staticFoo(Ljava/lang/String;)("Static field contains " + val3);
}-*/;

}
代码 10.20:从 JavaScript 调用 Java 方法和访问 Java 字段

3. 生成并运行 GWTHelloJavaCalledFromJavaScript 项目。

图 10.21: 从 JavaScript 代码调用 Java 实例的结果。


图 10.22: 从 JavaScript 代码第二次调用 Java 实例的结果。


图 10.23: 从 JavaScript 代码调用 Java 静态方法的结果


图 10.24: 从 JavaScript 代码访问 Java 实例字段的结果



图 10.25: 从 JavaScript 代码访问 Java 实例字段(第二次)的结果


图 10.26: 访问 Java 静态字段的结果

返回顶部


课外练习(针对 Sang Shin“AJAX 在线课程” 的学习者)


课外练习是要修改练习 4 中(在 GWTHello 项目上实现 RPC)构建的 GWTHelloRPC 项目,如下所示。您可以将新项目命名为任意名称,但此处我将称其为 GWTHelloMyOwnRPC

1. 将您自己的方法添加到该服务。建议使用的方法如代码 H.01 所示。将其他文件做相应修改。生成并运行应用程序,查看一切是否如预期运行。您可以让用户通过 UI 输入两个数字,或者您可以将数字硬编码入客户端代码。

public interface GWTService extends RemoteService{
public String myMethod(String s);
public int addNumber(int x, int y);
}
代码 H.01:要添加的推荐方法

需添加到 GWTServiceUsageExample.java 的代码段如代码 H.02 所示。

public class GWTServiceUsageExample extends VerticalPanel {

private Label lblServerReply = new Label();
private Label lblAddReply = new Label();
private TextBox txtUserInput = new TextBox();
private TextBox txtXInput = new TextBox();
private TextBox txtYInput = new TextBox();
private Button btnSend = new Button("Send to server");
private Button btnAdd = new Button("Add x + y");

public GWTServiceUsageExample() {
add(new Label("Input your text: "));
add(txtUserInput);
add(btnSend);
add(lblServerReply);
add(new Label("Input x and y: "));
add(new Label("Input x: "));
add(txtXInput);
add(new Label("Input y: "));
add(txtYInput);
add(btnAdd);
add(lblAddReply);

// Create an asynchronous callback to handle the result.
final AsyncCallback callback = new AsyncCallback() {
public void onSuccess(Object result) {
lblServerReply.setText((String)result);
}

public void onFailure(Throwable caught) {
lblServerReply.setText("Communication failed");
}
};

// Create an asynchronous callback to handle the result.
// Add your code here

// Listen for the button clicks
btnSend.addClickListener(new ClickListener(){
public void onClick(Widget w) {
// Make remote call. Control flow will continue immediately and later
// 'callback' will be invoked when the RPC completes.
getService().myMethod(txtUserInput.getText(), callback);
}
});

// Listen for the button clicks for addition
btnAdd.addClickListener(new ClickListener(){
public void onClick(Widget w) {
// Make remote call. Control flow will continue immediately and later
// 'callback' will be invoked when the RPC completes.
getService().addNumber(Integer.parseInt(txtXInput.getText()),
Integer.parseInt(txtYInput.getText()),callbackAdd);
}
});
}

public static GWTServiceAsync getService(){
// Create the client proxy. Note that although you are creating the
// service interface proper, you cast the result to the asynchronous
// version of
// the interface. The cast is always safe because the generated proxy
// implements the asynchronous interface automatically.
GWTServiceAsync service = (GWTServiceAsync) GWT.create(GWTService.class);
// Specify the URL at which our service implementation is running.
// Note that the target URL must reside on the same domain and port from
// which the host page was served.
//
ServiceDefTarget endpoint = (ServiceDefTarget) service;
String moduleRelativeURL = GWT.getModuleBaseURL() + "gwtservice";
endpoint.setServiceEntryPoint(moduleRelativeURL);
return service;
}
}
代码 H.02:建议用于 GWTServiceUsageExample.java 的代码

2.将以下文件以 AJAXHomework-ajaxgwtintro 作为 发送主题 发送至 ajaxhomework@sun.com






原文链接: http://www.javapassion.com/handsonlabs/ajaxgwtintro/