<!----><!----><!----><!---->
实现客户端功能
我们的StockWatcher 例子到目前为止已经很好了,回顾一下,我们用GWT的 widgets和 panels已经设计并实现了UI,并且为用户实现了 键盘单击事件 的初始化。现在,我们要为应用程序写客户端代码,让它实现功能。
验证用户输入
当用户第一次使用StockWatcher ,他需要通过在文本框里输入存货订单标示(一次只能增加一条)来向他的watchlist中增加存货。然而在我们增加一条存货的时候,我们需要检验新输入是否有效,如果无效,我们需要通过一个对话框来警告他。
业务顺序的第一步是给addStock()增加代码来完成创建存货。我们使用 TextBox widget的 getText() 方法来获得文本。GWT的 widgets 和panels包含了丰富的属性和方法,很多都是和HTML DOM元素相类似的。当我们转换用户在一个标准表单中的输入的时候,我们使用正则表达式来检查它的格式 (记住在java和JavaScript中使用正则表达式意思是一样的).
以下是更新后的addStock()方法:
private void addStock() {
final String symbol = newSymbolTextBox.getText().toUpperCase().trim();
newSymbolTextBox.setFocus(true);
// symbol must be between 1 and 10 chars thatare numbers, letters, or //dots
if (!symbol.matches("^[0-9a-zA-Z\\.]{1,10}$"))
{
Window.alert("'" + symbol + "'is not a valid symbol.");
newSymbolTextBox.selectAll();
return;
}
newSymbolTextBox.setText("");
// now we need to add the stock to the list...
}
importcom.google.gwt.user.client.Window;
现在,轻松了许多,重编译并运行StockWatcher application来测试输入验证。
如果你在hosted mode 下的浏览器已经打开了,你不需要重启它。只需要点击工具栏上的Refresh按钮来重载你更新后的GWT代码。
<!----><!---->  <!---->
操纵watch list
尽管StockWatcher已经有了验证存货输入的能力,但它仍然没有实现它的功能:用户依然不能看到增加的存货。现在让我们来继续完成功能吧。回顾一下,我们的UI包含了一个名为stocksFlexTable 的FlexTable窗口,它用于包含watch list。列表的每行都包含一个存货标识,当前价格,价格变动和一个用于移除存货的按钮。当前我们仅关注增加和移除存货功能,不用去担心设定价格和变更。
首先,我们需要一个数据结构来保存当前可见的存货标识列表,我们使用标准的Java ArrayList 来调用我们的存货列表:
public class StockWatcher implementsEntryPoint {
private VerticalPanel mainPanel = new VerticalPanel();
private FlexTable stocksFlexTable = new FlexTable();
private HorizontalPanel addPanel = new HorizontalPanel();
private TextBox newSymbolTextBox = new TextBox();
private Button addButton = new Button("Add");
private Label lastUpdatedLabel = new Label();
private ArrayList<String> stocks = newArrayList<String>();
别忘记了新增的import:
import java.util.ArrayList;
接下来是有趣的部分。向addStock()方法中增加如下的代码:
// don't add the stock if it'salready in the watch list
if (stocks.contains(symbol))
return;
// add the stock to the list
int row =stocksFlexTable.getRowCount();
stocks.add(symbol);
stocksFlexTable.setText(row, 0,symbol);
// add button to remove this stockfrom the list
Button removeStock = newButton("x");
removeStock.addClickListener(newClickListener(){
public void onClick(Widget sender) {
int removedIndex = stocks.indexOf(symbol);
stocks.remove(removedIndex);
stocksFlexTable.removeRow(removedIndex+1);
}
});
stocksFlexTable.setWidget(row, 3,removeStock);
现在的事实:运行或刷新hosted mode下的浏览器。得救了,你可以随意的添加和移除存货信息。现在我们唯一没有实现的功能是存货的价格及其变更。
at will意为随意
<!----><!----><img height="460" border="0" alt="" width="584" /><!---->
刷新存货价格
StockWatcher 最后且最重要的功能是更新我们关心的存货价格。如果我们用传统的web开发技术写应用程序的话,我们想更新价格需要每次都整个页面重载来响应。这可以用手或自动刷新(比如在HTML头部使用<meta http-equiv="refresh"content="5">标签)来实现。在web2.0中这是不够的,那些追捧崇拜的商人需要立马更新更新它们的存货价格,而不是烦人的页面刷新。
幸运的是,百忙中GWT让更新你的应用程序内容变得很容易。让我们使用 Timer 类来给StockWatcher增加自动更新存货价格的功能。
使用定时器自动刷新
为了使用Timer我们在我们的onModuleLoad()方法中创建了一个新的实例并重写了 run()方法。当定时器工作的时候run()方法将被调用。在我们的例子中,我们要调用一个将被写的名为refreshWatchList()的方法来实际进行更新操作。给 Timer 增加import,之后如下修改onModuleLoad()尾部的尾部:
Add the import for Timer andthen modify the end of the end of the onModuleLoad() method as follows:
importcom.google.gwt.user.client.Timer;
public void onModuleLoad() {
...
// add the main panel to the HTML element with the id"stockList"
RootPanel.get("stockList").add(mainPanel);
// setup timer to refresh list automatically
Timer refreshTimer = new Timer() {
public void run() {
refreshWatchList();
}
};
refreshTimer.scheduleRepeating(REFRESH_INTERVAL);
// move cursor focus to the text box
newSymbolTextBox.setFocus(true);
}
In addition to the change to onModuleLoad(), you'llalso need to define the constant that specifies the refresh rate. Add that tothe top of the StockWatcher class.
private static final intREFRESH_INTERVAL = 5000; // ms
在我们继续之前,我们需要增加调用新的refreshWatchList() 方法不止一次。在我们增加新的存货到FlexTable 后我们将在addStock()方法中立即调用它。(所以新的存货会马上获得它的价格及变更情况):
private void addStock() {
...
Button removeStock = new Button("x");
removeStock.addClickListener(new ClickListener(){
public void onClick(Widget sender) {
int removedIndex =stocks.indexOf(symbol);
stocks.remove(removedIndex);
stocksFlexTable.removeRow(removedIndex+1);
}
});
stocksFlexTable.setWidget(row, 3, removeStock);
// get stock price
refreshWatchList();
}
private void refreshWatchList() {
// Code will be added in the next section
}
StockPrice类
GWT中主要方式的一种就是在java语言中允许我们传播AJAX的开发。因此,我们可以采取静态类型检查和面向对象编程的嵌入时间(time-tested)模式,可以和现代的IDE特性结合,如代码补全和自动重构等,使得它以良好的代码管理为基础,比以往更加简单的去写健壮的AJAX应用程序。
对于StockWatcher来说,我们将存货价格数据放到它自己的类中进行重构。创建一个名为StockPrice 位于com.google.gwt.sample.stockwatcher.client包下的java类(in Eclipse, File -> New ->Class).
<!----><!----><img height="599" border="0" alt="" width="500" /><!---->
以下是我们的新类的完整实现:
packagecom.google.gwt.sample.stockwatcher.client;
public class StockPrice {
private String symbol;
private double price;
private double change;
public StockPrice() {
}
public StockPrice(String symbol, double price, double change) {
this.symbol = symbol;
this.price = price;
this.change = change;
}
public String getSymbol() {
return this.symbol;
}
public double getPrice() {
return this.price;
}
public double getChange() {
return this.change;
}
public double getChangePercent() {
return 10.0 * this.change / this.price;
}
public void setSymbol(String symbol) {
this.symbol = symbol;
}
public void setPrice(double price) {
this.price = price;
}
public void setChange(double change) {
this.change = change;
}
}
生成存货价格
既然我们有一个StockPrice 类来封装存货价格数据,让我们使用它来更新watch list表格。首先,我们需要生成实际数据。作为从在线数据源中检索真实的存货价格实际数据的替代,我们使用GWT内置的 Random类来创建伪随机价格并改变其值。我们将用这些值填充StockPrice对象数组,之后传给它们另一个功能来更新watch listFlexTable。向我们的StockWatcher类增加如下import和函数:
importcom.google.gwt.user.client.Random;
private void refreshWatchList() {
final double MAX_PRICE = 100.0; // $100.00
final double MAX_PRICE_CHANGE = 0.02; // +/- 2%
StockPrice[] prices = new StockPrice[stocks.size()];
for (int i=0; i<stocks.size(); i++) {
double price = Random.nextDouble() * MAX_PRICE;
double change = price * MAX_PRICE_CHANGE * (Random.nextDouble() * 2.0 -1.0);
prices = new StockPrice((String)stocks.get(i), price, change);
}
updateTable(prices);
}
更新watch list
最后的2个新函数会更新新价格的显示。在updateTable(StockPrice)方法中,你会发现例子中使用 NumberFormat 来格式话数值为字符串。在我们的例子中,我们用它以货币形式来格式化价格,且价格变动之前显示一个指示符。相似的,注意在updateTable(StockPrice[])方法中的使用 DateTimeFormat 类来格式化当前日期时间。接下来把这些功能加到StockWatcher中:
private voidupdateTable(StockPrice[] prices) {
for (int i=0; i<prices.length; i++) {
updateTable(prices);
}
// change the last update timestamp
lastUpdatedLabel.setText("Last update : " +
DateTimeFormat.getMediumDateTimeFormat().format(newDate()));
}
private void updateTable(StockPriceprice) {
// make sure the stock is still in our watch list
if (!stocks.contains(price.getSymbol())) {
return;
}
int row = stocks.indexOf(price.getSymbol()) + 1;
// apply nice formatting to price and change
String priceText = NumberFormat.getFormat("#,##0.00").format(price.getPrice());
NumberFormat changeFormat = NumberFormat.getFormat("+#,##0.00;-#,##0.00");
String changeText = changeFormat.format(price.getChange());
String changePercentText = changeFormat.format(price.getChangePercent());
// update the watch list with the new values
stocksFlexTable.setText(row, 1, priceText);
stocksFlexTable.setText(row, 2, changeText +" (" + changePercentText + "%)");
}
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import java.util.Date;
你可能已经注意到DateTimeFormat 和 NumberFormat 位于com.google.gwt.i18的子包中,看来它们是用来以某种方式来处理国际化问题。事实上它们也确实这么做的。当你格式化数组和日期的时候,二个类都自动的使用你的应用程序的locale设定。随后我们会在Getting Started guide中讨论更多 localizingand translating your GWT application(国际化和GWT应用程序的转换)
又到了测试我们的改变的时候了。运行或刷新应用程序在hosted mode,试着增加某种存货到watch list。现在,我们可以看到每个存货的当前价格和变动情况。如果你观察列表一段事件,你会发现那些值会随着底部的时间戳,每隔5秒中改变一次。
<!----><!----><img height="386" border="0" alt="" width="436" /><!---->
好吧,看起来像我们的StockWatcher 程序工作的很完美,不是吗?离你离开的时间越来越远,它证明了一个细微的bug。如果你找到它(hint: it's visible in the screenshot above).在下部分中,我们会使用我们的java调试技巧来调试它。
|