Sie sind auf Seite 1von 60

Step 1: Create a GWT Project

At this point, you've downloaded the most recent distribution of Google Web Toolkit. In this section, you'll create the StockWatcher project using either the Google Plugin for Eclipse or the GWT command-line utility webAppCreator. These utilities do the work of generating the project subdirectories and files you need to get started. To test that your project is configured correctly, you'll run the GWT starter application in development mode. Then you'll examine the project files that were created.

1.Create a GWT application. 2.Test the default project components. 3.Examine the project components.

1. Creating a GWT application


Creating the StockWatcher application (using Eclipse)
One of the benefits of using GWT is that you can leverage the tools, such as refactoring, code completion, and debugging, available in a Java IDE. In this tutorial, we use Eclipse because it is widely available and free. However, feel free to use your favorite IDE. The Google Plugin for Eclipse contains a wizard for creating GWT applications. Here are steps for creating a starter application. 1.In the toolbar, click the New Web Application Project button 2.Fill out the project details: 1.Enter the project name "StockWatcher". 2.Enter the package "com.google.gwt.sample.stockwatcher". 3.Make sure Use Google Web Toolkit is checked and that Use default SDK (GWT) is selected. .

4.(Optional) If you are using Google App Engine, make sure Use
default SDK (App Engine) is selected.

Google App Engine is checked and that Use

5.If you did not install the SDKs when you installed the Google Plugin for Eclipse, you should click Configure
SDKs... to specify the directory where GWT (and the App Engine SDK if necessary) was unzipped.

3.Click the Finish button.

Creating the StockWatcher application (without using Eclipse)


webAppCreator is a command-line tool included in the GWT download that generates the project subdirectories and files you need to get started. It creates a starter application, which you can run to ensure that all the components have been created and are hooked together correctly. As you develop your application, you'll replace the code for the starter application with your code. Starting with GWT 1.6, the former command-line tools projectCreator and applicationCreator have been combined into webAppCreator. For the StockWatcher project, you will run webAppCreator with the following parameters. Parameter -out Definition The directory to place the generated files. The fully-qualified path name to junit.jar on your system. You can download JUnit at their sourceforge project page, or you can use the version that ships with Eclipse. Example StockWatcher (PC) C:\eclipse\plugins\org.junit_3.8.2.v200706111738\junit.j ar (Mac) /Users/myname/eclipse/plugins/org.junit_3.8.2.v200706 111738/junit.jar

-junit

moduleNa me

The name of the GWT module you want to create.

com.google.gwt.sample.stockwatcher.StockWatcher

1.Create the StockWatcher application. At the command line, run webAppCreator. Enter the command below on a single line. (The example is shown on multiple lines only to improve readability.) Replace the junit.jar path name (highlighted in the example below) with the fully-qualified path name of junit.jar on your system.
webAppCreator -out StockWatcher -junit "C:\eclipse\plugins\org.junit_3.8.2.v200706111738\junit.jar" com.google.gwt.sample.stockwatcher.StockWatcher

Note: The -junit argument is optional. If you do not have junit installed on your system or do not wish to use junit in your application, you can leave it out. Tip: If you include the GWT command-line tools in your PATH environment variable, you won't have to invoke them by specifying their full path. 2.GWT webAppCreator generates the project subdirectories and files you need to get started.
Created directory StockWatcher/src Created directory StockWatcher/war Created directory StockWatcher/war/WEB-INF Created directory StockWatcher/war/WEB-INF/lib Created directory StockWatcher/src/com/google/gwt/sample/stockwatcher Created directory StockWatcher/src/com/google/gwt/sample/stockwatcher/client Created directory StockWatcher/src/com/google/gwt/sample/stockwatcher/server Created directory StockWatcher/test/com/google/gwt/sample/stockwatcher/client Created file StockWatcher/src/com/google/gwt/sample/stockwatcher/StockWatcher.gwt.xml Created file StockWatcher/war/StockWatcher.html Created file StockWatcher/war/StockWatcher.css Created file StockWatcher/war/WEB-INF/web.xml Created file StockWatcher/src/com/google/gwt/sample/stockwatcher/client/StockWatcher.java Created file StockWatcher/src/com/google/gwt/sample/stockwatcher/client/GreetingService.java Created file StockWatcher/src/com/google/gwt/sample/stockwatcher/client/GreetingServiceAsync.java Created file StockWatcher/src/com/google/gwt/sample/stockwatcher/server/GreetingServiceImpl.java Created file StockWatcher/build.xml Created file StockWatcher/README.txt Created file StockWatcher/test/com/google/gwt/sample/stockwatcher/client/StockWatcherTest.java Created file StockWatcher/.project Created file StockWatcher/.classpath Created file StockWatcher/StockWatcher.launch Created file StockWatcher/StockWatcherTest-dev.launch Created file StockWatcher/StockWatcherTest-prod.launch Created file StockWatcher/war/WEB-INF/lib/gwt-servlet.jar

Directories Created
/src/com/google/gwt/sample/stockwatcher Contains the GWT module definition and initial application files.

/test/com/google/gwt/sample/stockwatcher Contains JUnit test directory and a starter test class. /war Contains static resources that can be served publicly, such as image files, style sheets, and HTML host pages. /war/WEB-INF Contains Java web application files. /war/WEB-INF/lib Contains Java web application libraries. Starting with GWT 1.6, static files have been moved to /war.

Files Created
StockWatcher.gwt.xml GWT module definition StockWatcher.html host page StockWatcher.css application style sheet web.xml Java web application descriptor StockWatcher.java GWT entry point class GreetingService.java, GreetingServiceAsync.java, GreetingServiceImpl.java GWT sample RPC classes gwt-servlet.jar GWT server runtime library StockWatcherT est.java Starter test case for StockWatcher

Scripts Created
build.xml Ant build file for running the application in development mode or for invoking the GWT compiler from the command line.

Eclipse Files Created


.project .classpath StockWatcher.launch StockWatcherT est-dev.launch StockWatcherT est-prod.launch To see the complete list of options for webAppCreator, see Command-line Tools, webAppCreator. For more information on project structure, see the Developer's Guide, Directory/Package Conventions.

2. Testing the default project components


To check that all the project components were created, run the starter application in development mode. In development mode, you can interact with the application in a browser just as you would when it's eventually deployed.

Running the development mode code server (from Eclipse)


1.In the Package Explorer view, select the StockWatcher project. 2.In the toolbar, click the Run button (Run as Web Application). 3.When the development mode tab opens, right-click on the URL to copy it.

4.Paste the URL into your browser of choice.

Running the development mode code server (not from Eclipse)


webAppCreator creates an ant build file with a target for running the application in development mode. 1.From the command line, change to the StockWatcher directory 2.Execute: ant devmode Tip: If you include the Ant command-line tools in your PATH environment variable, you won't have to invoke them by specifying their full path. Development mode opens with two tabs: the development mode code server and the Jetty HTTP server. Press the "Launch Default Browser" button to launch StockWatcher in development mode using your default browser. Or, you can click "Copy to Clipboard" to copy the launch URL and paste it into the browser of your choice.

Connecting to the development mode code server (with and without Eclipse)
Once you have started the development mode (from Eclipse or using the build.xml script) and entered the URL into the browser, the browser will attempt to connect. If this is your first time running a GWT application in development mode, you may be prompted to install the Google Web Toolkit Developer Plugin. Follow the instructions on the page to install the plugin, then restart the browser and return to the same URL.

Starter Application
When you create a new web application with GWT, by default it creates a simple, starter application as shown below. This application helps you test that all the components are installed and configured before you start development. When you start writing the StockWatcher application, you'll replace this starter application code with your own.

3. Examining the project components


Let's examine some of the generated files and see how they fit together to form your GWT project.

The module XML file


Open the module XML file, StockWatcher/src/com/google/gwt/sample/stockwatcher/StockWatcher.gwt.xml. It contains the definition of the GWT module, the collection of resources that comprise a GWT application or a shared package. By default, StockWatcher inherits the core GWT functionality required for every project. Optionally, you can specify other GWT modules to inherit from.
<?xml version="1.0" encoding="UTF-8"?> <module rename-to='stockwatcher'> <!-- Inherit the core Web Toolkit stuff. <inherits name='com.google.gwt.user.User'/> -->

<!-- Inherit the default GWT style sheet.

You can change

--> --> -->

<!-- the theme of your GWT application by uncommenting <!-- any one of the following lines. <inherits name='com.google.gwt.user.theme.standard.Standard'/>

<!-- <inherits name="com.google.gwt.user.theme.chrome.Chrome"/> --> <!-- <inherits name="com.google.gwt.user.theme.dark.Dark"/> -->

<!-- Other module inherits

-->

<!-- Specify the app entry point class.

-->

<entry-point class='com.google.gwt.sample.stockwatcher.client.StockWatcher'/>

<!-- Specify the paths for translatable code <source path='client'/>

-->

</module>

In the module XML file, you specify your application's entry point class. In order to compile, a GWT module must specify an entry point. If a GWT module has no entry point, then it can only be inherited by other modules. It is possible to include other modules that have entry points specified in their module XML files. If so, then your module would have multiple entry points. Each entry point is executed in sequence. By default, StockWatcher uses two style sheets: the default GWT style sheet, standard.css (which is referenced via the inherited theme), and the application style sheet, StockWatcher.css which was generated by webAppCreator. Later in this tutorial, you'll learn how to override the default GWT styles.

The Host Page


Open the host page, StockWatcher/war/StockWatcher.html. The code for a web application executes within an HTML document. In GWT, we call this the host page. For example, the host page for the StockWatcher project is StockWatcher.html. The host page references the application style sheet, StockWatcher.css. The host page references the path of JavaScript source code (generated by GWT) responsible for the dynamic elements on the page. The contents of the entire body element can be generated dynamically, for example, as it is with starter application. However, when you implement the StockWatcher application, you will use a mix of static and dynamic elements. You'll create an HTML <div> element to use as placeholder for the dynamically generated portions of the page.

Selecting Quirks Mode vs. Standards Mode


To provide better cross-browser compatibility, GWT sets the doctype declaration to HTML 4.01 Transitional. This, in turn, sets the browser's rendering engine to "Quirks Mode". If you instead want to render the application in "Standards Mode", there are a number of other doctypes you can use to force the browser to this render mode. In general, GWT applications will work in "Standards Mode" just as well as "Quirks Mode", but in some cases using widgets like panels and such may not render correctly. This problem has been greatly improved since GWT 1.5, and more work is being done to solve this problem once and for all.

Preserving Browser History


GWT provides a mechanism for helping your application meet users' expectations of a web page, specifically in their ability to use the browser's back button in such situations as a multi-page wizard or a shopping cart/checkout scenario. The host page contains the iframe tag necessary for incorporating history support in your GWT application. To learn more about managing browser history in a GWT application, see the Developer's Guide, History.

The Application Style Sheet


Open the application style sheet, StockWatcher/war/StockWatcher.css. A style sheet is associated with each project. By default, the application style sheet, StockWatcher.css, contains style rules for the starter application. In the Applying Stylesection of this tutorial, you'll replace the style rules for the starter application, with the style rules for the StockWatcher application.

Just as for any web page, you can specify multiple style sheets. List multiple style sheets in their order of inheritance; that is, with the most specific style rules in the last style sheet listed.

The Java source code


Open the source for the StockWatcher entry point class, StockWatcher/src/com/google/gwt/sample/stockwatcher/client/StockWatcher.java. Currently, StockWatcher.java contains the Java source for the starter application. In this tutorial, you'll replace this code with the client-side code for StockWatcher. The StockWatcher class implements the GWT interface EntryPoint. It contains the method onModuleLoad. Because the StockWatcher class is specified as the entry point class in StockWatcher's module definition, when you launch StockWatcher the onModuleLoad method is called. The StockWatcher class inherits functionality via other GWT modules you included in StockWatcher's module definition (StockWatcher.gwt.xml). For example, when building the user interface, you'll be able to include types and resources from the package com.google.gwt.user.client.ui because it is part of the GWT core functionality included in the GWT module com.google.gwt.user.User.

What's Next
At this point you've created the stub files for the StockWatcher application and loaded the project into Eclipse (or whatever Java IDE you prefer). Now you're ready to design the StockWatcher application. Step 2: Designing the Application

Step 2: Design the Application


At this point, you've created the stub files you need to start coding StockWatcher. In this section, you'll review the functional requirements and design the user interface.

1.Examine the functional requirements. 2.Identify the elements of the UI design.

1. Examining the functional requirements


Initially you want the StockWatcher application to do six things. Provide users with the ability to add stocks. (Supply simple validation on input for illegal characters or existing stock.) Display the following information for each stock: symbol, price, change since last refresh. Provide users with the ability to delete a stock from the list. Refresh the stock price. Calculate the change since the last refresh as both a number and a percentage. Display a timestamp indicating the last update.

2. Identifying the elements of the UI design

After studying StockWatcher's functional requirements, you decide you need these UI elements: a table to hold the stock data two buttons, one to add stocks and one to remove them an input box to enter the stock code a timestamp to show the time and date of the last refresh The design team has suggested the following additions: a logo a header colors to indicate whether the change in price was positive or negative

Including Static Elements


GWT does not dictate how you lay out your HTML page. A GWT application can take up the entire browser window, as it does in the startup application, or it can be embedded in an existing page, as it is in the Build a Sample GWT Application page of this tutorial. The StockWatcher application contains both static and dynamic elements. The Google Code logo and the header "StockWatcher" are static elements in the HTML host page. All the other elements are created programmatically using GWT widgets and panels.

What's Next
At this point you've reviewed StockWatcher's functional requirements. You have a clear idea of what StockWatcher does. You know what UI elements you need to implement and how you want to lay them out. Now you're ready to build the user interface using GWT widgets and panels. Step 3: Building the User Interface

Step 3: Build the User Interface


At this point, you've created the components of the StockWatcher project and reviewed its functional requirements and UI design. In this section, you'll build the user interface out of GWT widgets and panels.

1.Select the GWT widgets needed to implement the UI elements. 2.Select the GWT panels needed to lay out the UI elements. 3.Embed the application in the host page, StockWatcher.html. 4.Implement the widgets and panels in StockWatcher.java. 5.Test the layout in development mode.
GWT shields you from worrying too much about cross-browser incompatibilities. If you construct the interface with GWT widgets and composites, your application will work on the most recent versions of Chrome, Firefox, Internet Explorer, Opera, and Safari. However, DHTML user interfaces remain remarkably quirky; therefore, you still must test your applications thoroughly on every browser.

1. Selecting GWT widgets to implement the UI elements


First, look at the Widget Gallery and select the GWT widget for each UI element. In the Widget Gallery the widgets have a default style and so they don't look exactly as they will in the final implementation of StockWatcher. Don't worry about that now. First you'll focus on getting the widgets working. Later, you will change their appearance with CSS in the Applying Syles section.

Stock Data Table


GWT provides a special table widget called a FlexTable. The FlexTable widget creates cells on demand. This is just what you need for the table containing the stock data because you don't know how many stocks the user will add. A table implemented with the FlexTable widget will expand or collapse as the user adds or removes stocks.

Buttons
Whenever possible, GWT defers to a browser's native user interface elements. For instance, a Button widget becomes a true HTML <button> rather than a synthetic button-like widget that's built, for example, from a <div>. This means that GWT buttons render as designed by the browser and client operating system. The benefit of using native browser controls is that they are fast, accessible, and most familiar to users. Also, they can be styled with CSS.

Input Box
GWT provides several widgets to create fields that users can type in: TextBox widget, a single-line text box PassWordTextBox widget, a text box that visually masks input TextArea widget, a multi-line text box SuggestBox, a text box that displays a pre-configured set of items StockWatcher users will type in a stock code which is single line of text; therefore, implement a TextBox widget.

Label
In contrast with the Button widget, the Label widget does not map to the HTML <label> element, used in HTML forms. Instead it maps to a <div> element that contains arbitrary text that is not interpreted as HTML. As a <div> element, it is a block-level element rather than an inline element.
<div class="gwt-Label">Last update : Oct 1, 2008 1:31:48 PM</div>

If you're interested in taking a peek at the API reference for the GWT widgets you'll use to build the StockWatcher interface, click on the links in the table below. UI element a table to hold the stock data two buttons, one to add stocks and one to remove them an input box to enter the stock code a timestamp to show the time and date of the last refresh a logo a header colors to indicate whether the change in price was positive or negative GWT implementation FlexTable widget Button widget TextBox widget Label widget image file referenced from HTML host page static HTML in HTML host page dynamic CSS

In Depth: If you don't find a widget that meets the functional requirements of your application, you can create your own. For details on creating composite widgets or widgets from scratch using Java or JavaScript, see the Developer's Guide, Creating Custom Widgets.

2. Selecting GWT panels to lay out the UI elements


Now that you know what widgets you'll use, you'll decide how to lay them out using GWT panels. GWT provides several types of panels to manage the layout. Panels can be nested within other panels. This is analogous to laying out your web page in HTML using nested div elements or tables. For StockWatcher, you'll use a horizontal panel nested within a vertical panel.

Horizontal Panel
The two elements used to add a stockthe input box for typing in a new stock symbol and the Add buttonare closely related functionally and you want keep them together visually. To lay them out side-by-side, you'll put the TextBox widget and a Button widget in a horizontal panel. In the Java code, you'll create a new instance ofHorizontalPanel and name it addPanel.

Vertical Panel
You want to lay out the remaining elements vertically: the FlexTable widget for the stock table the Add Stock panel, which contains the input box and Add button the Label widget for the timestamp You'll do this with a vertical panel. In the Java code, you'll create a new instance of VerticalPanel and name it mainPanel.

Root Panel
There is one more panel you need that is not visible in the user interface: a Root panel. A Root panel is the container for the dynamic elements of your application. It is at the top of any GWT user interface hierarchy. There are two ways you can use a Root panel, either to generate the entire body of the page or to generate specific elements embedded in the body. The Root panel works by wrapping the <body> or other element in the HTML host page. By default (that is, if you don't add any placeholders in the host page) the Root panel wraps the <body> element. However, you can wrap any element if you give it an id and then, when you call the Root panel, pass the id as a parameter. You'll see how this works in the next two sections when you do it for StockWatcher.
RootPanel.get() RootPanel.get("stockList") // Default. Wraps the HTML body element. // Wraps any HTML element with an id of "stockList"

A host page can contain multiple Root panels. For example, if you're embedding multiple GWT widgets or panels into a host page, each one can be implemented independently of the others, wrapped in its own Root panel.

3. Embedding the application in the host page


To get the StockWatcher application to run in the browser, you need to embed it in an HTML file, the HTML host page. The host page for the StockWatcher project, StockWatcher.html, was generated by webAppCreator. For the starter application, StockWatcher.html had an empty body element. As a result, the Root panel wrapped the entire body element. The text input box, label ("Please enter your name:") and "Send" button were build dynamically with GWT. If your application has no static elements, you wouldn't need to edit the HTML host page at all. However, for StockWatcher you will use some static HTML text (for the header) and an image (for the logo) in addition to the dynamic elements. You will embed the GWT application in the browser page using a placeholder, a <div> element with an id of "stockList". This implementation strategy is especially useful for embedding GWT into an existing application. As shown in the following code, do the following: 1.Open the host page, StockWatcher/war/StockWatcher.html. 2.In the head element, change the title text to StockWatcher. 3.In the body element, add an <h1> heading, StockWatcher. 4.In the body element, add a <div> element and give it an id of stockList. 5.Delete the unneeded elements from the starter project application. 6.Save the file StockWatcher.html.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <link type="text/css" rel="stylesheet" href="StockWatcher.css"> <title>StockWatcher</title> <script type="text/javascript" language="javascript" src="stockwatcher/stockwatcher.nocache.js"></script> </head> <body>

<h1>StockWatcher</h1> <div id="stockList"></div> <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe> <noscript> <div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif"> Your web browser must have JavaScript enabled in order for this application to display correctly. </div> </noscript>

<h1>Web Application Starter Project</h1> <table align="center"> <tr> <td colspan="2" style="font-weight:bold;">Please enter your name:</td>

</tr> <tr> <td id="nameFieldContainer"></td> <td id="sendButtonContainer"></td> </tr> </table> </body> </html>

Note: HTML comments have been omitted for brevity.

4. Implementing widgets and panels


Next you will construct the user interface from GWT widgets and panels. You want the UI to display as soon as StockWatcher starts up, so you'll implement them in the onModuleLoad method. In this section, you will: 1.Instantiate each widget and panel. 2.Create the table that holds the stock data. 3.Lay out the widgets using the Add Stock (horizontal) panel and the Main (vertical) panel. 4.Associate the Main panel with the Root panel. 5.Move the cursor focus to the input box. You can follow this section of the tutorial step-by-step, or you can cut and paste the entire block of code from the Summary at the end.

1. Instantiate each widget and panel


1.Instantiate each widget and panel using class field initializers. Open StockWatcher/src/com/google/gwt/sample/stockwatcher/client/StockWatcher.java. In StockWatcher.java, replace all the existing code for the starter application (from the imports down to the handler) with the following code.
package com.google.gwt.sample.stockwatcher.client; public class StockWatcher implements EntryPoint { private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button("Add"); private Label lastUpdatedLabel = new Label();

/** * Entry point method. */ public void onModuleLoad() { // TODO Create table for stock data.

// TODO Assemble Add Stock panel. // TODO Assemble Main panel. // TODO Associate the Main panel with the HTML host page. // TODO Move cursor focus to the input box.

Along the left edge, Eclipse flags the variable definitions with a red "x" because their types are undefined. Tip: One way you can leverage Eclipse is to use its "suggest" feature to add the required import declarations, as follows. 2.Display suggested corrections by clicking on the first red "x". Select "import EntryPoint (com.google.gwt.core.client.EntryPoint)" by pressing return. 3.Resolve all the other errors by declaring the import declarations in the same way. If you are not using Eclipse, cut and paste from the highlighted code below.
package com.google.gwt.sample.stockwatcher.client;

import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel;

public class StockWatcher implements EntryPoint {

private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button("Add"); private Label lastUpdatedLabel = new Label();

/** * Entry point method. */ public void onModuleLoad() { // TODO Create table for stock data. // TODO Assemble Add Stock panel. // TODO Assemble Main panel. // TODO Associate the Main panel with the HTML host page. // TODO Move cursor focus to the input box.

2. Create a table for stock data


Implement the table that will hold the stock data. Set up the header row of the table. To do this, use the setText method to create labels in the heading of each column: Symbol, Price, Change, Remove. 1.Create a table for stock data. In the onModuleLoad method, replace the TODO comment with the highlighted code.
package com.google.gwt.sample.stockwatcher.client;

import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel;

public class StockWatcher implements EntryPoint {

private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button("Add"); private Label lastUpdatedLabel = new Label();

/** * Entry point method. */ public void onModuleLoad() { // Create table for stock data. stocksFlexTable.setText(0, 0, "Symbol"); stocksFlexTable.setText(0, 1, "Price"); stocksFlexTable.setText(0, 2, "Change"); stocksFlexTable.setText(0, 3, "Remove");

// TODO Assemble Add Stock panel. // TODO Assemble Main panel. // TODO Associate the Main panel with the HTML host page. // TODO Move cursor focus to the input box.

You can see that adding to a table can be accomplished with a call to the setText method. The first parameter indicates the row, the second the column, and the final parameter is the text that will be displayed in the table cell.

3. Lay out the widgets


To lay out the widgets, you'll assemble two panels, the Add Stock panel and the Main panel. First assemble the Add Stock panel, a horizontal panel that wraps the input box and Add button. Then assemble the Main panel, a vertical panel that specifies the layout of the stock list table, the Add Stock panel, and the timestamp. 1.Lay out the widgets in the Add Stock panel and the Main panel. In the onModuleLoad method, replace the TODO comment with the highlighted code.
package com.google.gwt.sample.stockwatcher.client;

import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel;

public class StockWatcher implements EntryPoint {

private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button("Add"); private Label lastUpdatedLabel = new Label();

/** * Entry point method. */ public void onModuleLoad() { // Create table for stock data. stocksFlexTable.setText(0, 0, "Symbol"); stocksFlexTable.setText(0, 1, "Price"); stocksFlexTable.setText(0, 2, "Change"); stocksFlexTable.setText(0, 3, "Remove");

// Assemble Add Stock panel. addPanel.add(newSymbolTextBox); addPanel.add(addStockButton);

// Assemble Main panel. mainPanel.add(stocksFlexTable); mainPanel.add(addPanel); mainPanel.add(lastUpdatedLabel);

// TODO Associate the Main panel with the HTML host page. // TODO Move cursor focus to the input box.

4. Associate the Main panel with the Root panel


In order for any GWT widget or panel to be embedded in the HTML host page, it must be contained within a Root panel. Associate the Root panel with Vertical panel assigned to mainPanel. The Root panel wraps the HTML element (in StockWatcher's host page) that has an id of "stocklist". In this case, it is a <div> element. 1.Associate the Main panel with the host page via the Root panel. In the onModuleLoad method, replace the TODO comment with the highlighted code.
package com.google.gwt.sample.stockwatcher.client;

import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel;

public class StockWatcher implements EntryPoint {

private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button("Add"); private Label lastUpdatedLabel = new Label();

/** * Entry point method. */ public void onModuleLoad() { // Create table for stock data. stocksFlexTable.setText(0, 0, "Symbol");

stocksFlexTable.setText(0, 1, "Price"); stocksFlexTable.setText(0, 2, "Change"); stocksFlexTable.setText(0, 3, "Remove");

// Assemble Add Stock panel. addPanel.add(newSymbolTextBox); addPanel.add(addStockButton);

// Assemble Main panel. mainPanel.add(stocksFlexTable); mainPanel.add(addPanel); mainPanel.add(lastUpdatedLabel);

// Associate the Main panel with the HTML host page. RootPanel.get("stockList").add(mainPanel);

// TODO Move cursor focus to the input box.

Eclipse flags RootPanel and suggests the correct import declaration. 2.Include the import declaration.
import com.google.gwt.user.client.ui.RootPanel;

5. Move cursor focus to the input box


Finally, move the cursor focus to the input box so, when StockWatcher loads, the user can begin adding stocks. 1.In the onModuleLoad method, replace the TODO comment with the highlighted code.
package com.google.gwt.sample.stockwatcher.client;

import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel;

public class StockWatcher implements EntryPoint {

private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel();

private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button("Add"); private Label lastUpdatedLabel = new Label();

/** * Entry point method. */ public void onModuleLoad() { // Create table for stock data. stocksFlexTable.setText(0, 0, "Symbol"); stocksFlexTable.setText(0, 1, "Price"); stocksFlexTable.setText(0, 2, "Change"); stocksFlexTable.setText(0, 3, "Remove");

// Assemble Add Stock panel. addPanel.add(newSymbolTextBox); addPanel.add(addStockButton);

// Assemble Main panel. mainPanel.add(stocksFlexTable); mainPanel.add(addPanel); mainPanel.add(lastUpdatedLabel);

// Associate the Main panel with the HTML host page. RootPanel.get("stockList").add(mainPanel);

// Move cursor focus to the input box. newSymbolTextBox.setFocus(true);

Summary
Here's what you've done to this point.
package com.google.gwt.sample.stockwatcher.client;

import com.google.gwt.core.client.EntryPoint; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootPanel;

import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel;

public class StockWatcher implements EntryPoint {

private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button("Add"); private Label lastUpdatedLabel = new Label();

/** * Entry point method. */ public void onModuleLoad() { // Create table for stock data. stocksFlexTable.setText(0, 0, "Symbol"); stocksFlexTable.setText(0, 1, "Price"); stocksFlexTable.setText(0, 2, "Change"); stocksFlexTable.setText(0, 3, "Remove");

// Assemble Add Stock panel. addPanel.add(newSymbolTextBox); addPanel.add(addStockButton);

// Assemble Main panel. mainPanel.add(stocksFlexTable); mainPanel.add(addPanel); mainPanel.add(lastUpdatedLabel);

// Associate the Main panel with the HTML host page. RootPanel.get("stockList").add(mainPanel);

// Move cursor focus to the input box. newSymbolTextBox.setFocus(true);

5. Testing the layout


One benefit of using GWT in your AJAX application development is that you can see the effects of your code changes as soon as you refresh the browser running development mode. So that you can see your changes whether you are developing or

debugging, in Eclipse, run StockWatcher in debug mode. Then you'll be able to switch between Java and Debug perspectives without having to relaunch StockWatcher. 1.Save the edited file: Save StockWatcher.java 2.If the StockWatcher project is still running from the startup application, stop it by going to the Development Mode tab an clicking on the red square in its upper right corner, whose tooltip says "Terminate Selected Launch", and then the gray "XX" to its right, whose tooltip says "Remove All Terminated Launches". It may take a minute for it to complete, before you can do the next step. 3.Launch StockWatcher in development mode. From the Eclipse menu bar, select Run > Debug As > Web Application If you are not using Eclipse, from the command line enter ant devmode 4.The browser displays your first iteration of the StockWatcher application. The button will not work until we later implement it.

StockWatcher displays the header of the flex table, the input box, and the Add button. You haven't yet set the text for the Label, so it isn't displayed. You'll do that after you've implemented the stock refresh mechanism. 5.Leave StockWatcher running in development mode. In the rest of this tutorial, you'll frequently be testing changes in development mode.

Refreshing Development Mode


You do not always need to relaunch your application in development mode after modifying your source code. Instead, just click the browser's Refresh button after saving your changes, and the code server will automatically recompile your application and open the new version. Best Practices: You may notice that your changes take effect sometimes even if you do not refresh development mode. This behavior is a result of the way development mode interacts with the compiled code, but it is not always reliable. Specifically, it happens only when you make minor changes to existing functions. To ensure your changes are included, make it a habit to always refresh the browser after making changes.

What's Next
At this point you've built the basic UI components of StockWatcher by implementing GWT widgets and panels. The widgets don't respond to any input yet.

Now you're ready to code event handling on the client. You'll wire up the widgets to listen for events and write the code that responds to those events. Step 4: Managing Events on the Client

Step 4: Manage Events on the Client


At this point, you've created all the elements of the interface. Like many user interface frameworks, GWT is event-based. This means that the code executes in response to some event occurring. Most often, that event is triggered by the user, who uses the mouse or keyboard to interact with the application interface. In this section, you'll wire up your widgets to listen for and handle mouse and keyboard events.

1.Review the functional requirements. 2.Listen for events. 3.Respond to events. 4.Test event handling.

1. Reviewing the requirements for event handling


Let's review the StockWatcher requirements to see what events occur. Task UI Event (Trigger mechanism) Clicks the Add button or presses return in the input box. Presses the Remove button. Response Verify input. Check if stock already exists. Add a new row. Create a delete button. Remove row from table.

User enters a stock code.

User deletes stock from the table.

GWT provides a number of different event handler interfaces. To handle click events on the Add and Remove buttons, you'll use the ClickHandler interface. To handle keyboard events in the input box, you'll use the KeyPressHandler interface. Starting with GWT 1.6, the ClickHandler, KeyDownHandler, KeyPressHandler, and KeyUpHandler interfaces have replaced the now deprecated ClickListener and KeyBoardListener interfaces.

2. Listening for events


Event Handler Interfaces
Events in GWT use the event handler interface model similar to other user interface frameworks. To subscribe to an event, you pass a particular event handler interface to the appropriate widget. An event handler interface defines one or more methods that the widget then calls to announce (publish) an event.

Handling Mouse Events


One way StockWatcher users can enter a stock code is by using their mouse to click on the Add button. You'll handle the Add button's click event by passing it an object that implements the ClickHandler interface. In this case, you'll use an anonymous inner class to implement ClickHandler. The ClickHandler interface has one method, onClick, which fires when the user clicks on the widget. When the user clicks on the Add button, StockWatcher should respond by adding the stock to the stock table. So, to handle the click event, call the addStock method. You haven't written the addStock method yet; you'll create a stub and then code it in the next section.

1.Add an event handler to the Add button so it can receive click events. In Stockwatcher.java, in the onModuleLoad method, cut and paste the code commented "Listen for mouse events on the Add button." that is highlighted below. Eclipse flags ClickHandler and suggests you include the import declaration. 2.Include the import declarations for ClickHandler and ClickEvent. Eclipse flags addStock. 3.In StockWatcher.java, create the stub for the addStock method. Select the Eclipse shortcut, Create method addStock() in type 'StockWatcher'. Or copy and paste from the code highlighted below. Note: Depending on your Eclipse configuration, it might create the addStock method with an access modifier of protected. You aren't going to subclass StockWatcher, so later when you implement the addStock method, you'll change its access to private.
package com.google.gwt.sample.stockwatcher.client;

import com.google.gwt.core.client.EntryPoint; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel;

public class StockWatcher implements EntryPoint {

private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button("Add"); private Label lastUpdatedLabel = new Label();

/** * Entry point method. */ public void onModuleLoad() { // Create table for stock data. stocksFlexTable.setText(0, 0, "Symbol"); stocksFlexTable.setText(0, 1, "Price"); stocksFlexTable.setText(0, 2, "Change"); stocksFlexTable.setText(0, 3, "Remove");

// Assemble Add Stock panel.

addPanel.add(newSymbolTextBox); addPanel.add(addStockButton);

// Assemble Main panel. mainPanel.add(stocksFlexTable); mainPanel.add(addPanel); mainPanel.add(lastUpdatedLabel);

// Associate the Main panel with the HTML host page. RootPanel.get("stockList").add(mainPanel);

// Move cursor focus to the input box. newSymbolTextBox.setFocus(true);

// Listen for mouse events on the Add button. addStockButton.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { addStock(); } });

/** * Add stock to FlexTable. Executed when the user clicks the addStockButton or * presses enter in the newSymbolTextBox. */ private void addStock() { // TODO Auto-generated method stub

Implementation Note: For smaller applications, such as StockWatcher, that handle relatively few events, using anonymous inner classes gets the job done with minimal coding. However, if you have large number of event handlers subscribing to events, this approach can be inefficient because it could result in the creation of many separate event handler objects. In that case, it's better to have a class implement the event handler interface and handle events coming from multiple event publishers. You can distinguish the source of the event by calling its getSource() method. This makes better use of memory but requires slightly more code. For a code example, see the Developer's Guide, Event Handlers.

Handling Keyboard Events


In addition to using the Add button, StockWatcher users can enter a stock code without taking their hands from the keyboard by pressing return in the input box.

To subscribe to keyboard events, you can call the addKeyPressHandler(KeyPressHandler) method and pass it a KeyPressHandler. 1.Hook up the keypress event handler for the input box, newSymbolTextBox. In the onModuleLoad method, cut and paste the code commented "Listen for keyboard events in the input box." that is highlighted below.
// Listen for mouse events on the Add button. addStockButton.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { addStock(); } });

// Listen for keyboard events in the input box. newSymbolTextBox.addKeyPressHandler(new KeyPressHandler() { public void onKeyPress(KeyPressEvent event) { if (event.getCharCode() == KeyCodes.KEY_ENTER) { addStock(); } } });

/** * Add stock to FlexTable. Executed when the user clicks the addStockButton or * presses enter in the newSymbolTextBox. */ private void addStock() { // TODO Auto-generated method stub

Eclipse flags KeyPressHandler and suggests you include the import declaration. 2.Include the import declarations.
import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressHandler;

The event handlers are now wired up and ready for an event. Your next step is to fill out the stub addStock method.

3. Responding to user events


At this point, StockWatcher should be listening for user input, a mouse or keyboard event that signals the user has entered a stock code. So next you'll test whether or not the event handler interfaces are working by coding the response that StockWatcher

should make when it detects an event: add the stock. StockWatcher responds on the client side without sending any requests back to server or reloading the HTML page.

Adding the stock to the stock table


In StockWatcher, users will enter the stock codes of the stocks they want to monitor one at a time into the input box. When they press Enter or click on the Add button, you want StockWatcher to respond as follows: 1.Validate the input. 2.Check for duplicates. 3.Add the stock. 4.Add a button for removing the stock from the list. In this section, you'll code the first response, validating the input, just to see if the event handler interfaces are working. In the next section, Coding Functionality on the Client, you'll code the rest of the steps to add the stock. You'll implement this functionality in the addStock method.

Validating input in a text box


You want verify that the stock code entered is valid. Rather than verify whether the user input matches an actual stock code, for the purposes of this tutorial, you'll just perform a simple character validity check. First, extract the stock code. To retrieve the text in the TextBox widget use its getText method. Next, ensure that the charcters are not within the set of illegal characters you specify. After you've converted the user input to a standard form, use a regular expression to check its format. Remember to use regular expressions that have the same meaning in both Java and JavaScript. If the input is valid, clear the text box so the user can add another stock code. Finally, if the input is not valid, warn users via a dialog box. 1.Validate user input of the stock code. In StockWatcher.java. replace the stub addStock method with following code.
private void addStock() { final String symbol = newSymbolTextBox.getText().toUpperCase().trim(); newSymbolTextBox.setFocus(true);

// Stock code must be between 1 and 10 chars that are numbers, letters, or dots. if (!symbol.matches("^[0-9A-Z\\.]{1,10}$")) { Window.alert("'" + symbol + "' is not a valid symbol."); newSymbolTextBox.selectAll(); return; }

newSymbolTextBox.setText("");

// TODO Don't add the stock if it's already in the table.

// TODO Add the stock to the table.

// TODO Add a button to remove this stock from the table.

// TODO Get the stock price.

Eclipse flags Window and suggests you include the import declaration. 2.Include the import declaration.
import com.google.gwt.user.client.Window;

4. Testing event handling


At this point you should be able to enter text in the input box. If you use an illegal character, a dialog box should pop up and display a warning. Try it and see. 1.Test event handling in development mode. Press Refresh in the already open browser. 2.Test that both event handler interfaces work. Enter stock codes in the input box. Enter using both methods, by pressing return and by using the mouse to click on the Add button. At this point, the stock is not added to the table. However, the input box should clear so that you can add another stock. 3.Test the validity checking and error message. Make some typos that include illegal characters.

Tip: Changes made to your Java code are immediately shown in the browser after pressing refresh. If development mode is already running, you don't need to restart it. Just click the Refresh button in your browser to reload your updated GWT code. Although you have not compiled StockWatcher yet, you can test it in production mode here: Run StockWatcher

What's Next
At this point you've implemented event handler interfaces for mouse and keyboard events that signal the user has entered a stock. Stockwatcher responds by validating the input. Now you're ready to implement the code on the client that adds the stock to the table and provide a button to remove it. You will also display the stock prices and display data and a timestamp showing when the data was last updated. Step 5: Coding Functionality on the Client

Step 5: Code Functionality on the Client


At this point, you've built the user interface from GWT widgets and panels and wired in the event handlers. StockWatcher accepts input but it doesn't yet add the stock to the stock table or update any stock data. In this section, you'll finish implementing all of StockWatcher's client-side functionality. Specifically, you'll write the code to the following:

1.Add and remove stocks from the stock table. 2.Refresh the Prices and Change fields for each stock in the table. 3.Implement the timestamp showing the time of last update.
Your initial implementation of StockWatcher is simple enough that your can code all its functionality on the client side. Later you'll add calls to the server to retrieve the stock data.

1. Adding and removing stocks from the stock table


Your first task is to add the stock code and a Remove button to the stock table. Remember, the FlexTable will automatically resize to hold the data, so you don't have to worry about writing code to handle that. A.Create a data structure. B.Add rows to the stock table. C.Add a button to remove stocks from the stock table. D.Test in development mode.

A. Create a data structure


You need a data structure to hold the list of stock symbols the user has entered. Use the standard Java ArrayList and call the list stocks. 1.Create a data structure. In StockWatcher.java, in the StockWatcher class, create a new instance of a Java ArrayList.
public class StockWatcher implements EntryPoint {

private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button("Add"); private Label lastUpdatedLabel = new Label(); private ArrayList<String> stocks = new ArrayList<String>();

2.Eclipse flags ArrayList and suggests you include the import declaration. 3.Include the import declaration.
import java.util.ArrayList;

B. Add rows to the flex table


After the user enters a stock code, first check to make sure it's not a duplicate. If the stock code doesn't exist, add a new row to the FlexTable and populate the cell in the first column (column 0) with the stock symbol that the user entered. To add text to a cell in the FlexTable, call the setText method. 1.Check the stock to see if it exists and if it does, don't add it again. In the addStock method, replace the TODO comment with this code.
// Don't add the stock if it's already in the table. if (stocks.contains(symbol)) return;

2.If the stock doesn't exist, add it. In the addStock method, replace the TODO comment with this code.
// Add the stock to the table. int row = stocksFlexTable.getRowCount(); stocks.add(symbol); stocksFlexTable.setText(row, 0, symbol);

When you call the setText method, the FlexTable automatically creates new cells as needed; therefore, you don't need to resize the table explicitly.

C. Add a button to remove stocks from the stock list


So that users can delete a specific stock from the list, insert a Remove button in the last cell of the table row. To add a widget to a cell in the FlexTable, call the setWidget method. Subscribe to click events with the addClickHandler method. If the Remove Stock button publishes a click event, remove the stock from the FlexTable and the ArrayList. 1.Add the button for deleting the stock from the list. In the addStock method, replace the TODO comment with this code.
// Add a button to remove this stock from the table. Button removeStockButton = new Button("x"); removeStockButton.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { int removedIndex = stocks.indexOf(symbol); stocks.remove(removedIndex); } }); stocksFlexTable.setWidget(row, 3, removeStockButton); stocksFlexTable.removeRow(removedIndex + 1);

D. Test Add/Remove Stock Functionality


You have one more TODO to code: get the stock price. But first do a quick check in development mode to see if Add Stock and Remove Stock functionality is working as expected. At this point when you enter a stock code, StockWatcher should add it to the stock table. Try it and see. 1.Run StockWatcher in development mode. Press Refresh in the already open browser. 2.Add a stock. Enter stock codes in the input box. StockWatcher should add the stock to the table. The table resizes to hold the new data. However, the Price and Change fields are still empty. If you enter a stock code in lowercase, it converts the letters to uppercase. 3.Verify that you can't add duplicate stocks to the table.

Add a stock code that already exist in the table. StockWatcher should clear the input box but not add the the same stock code again. 4.Delete a stock. Click the Remove button. The stock is deleted from the table and the table resizes.

Now you'll tackle that last TODO: get the stock price.

2. Refreshing the Price and Change fields


The most important feature of StockWatcher is updating the prices of the stocks the users are watching. If you were writing StockWatcher using traditional web development techniques, you would have to rely on full page reloads every time you wanted to update the prices. You could accomplish this either manually (making the user click the browser's Refresh button) or automatically (for example, using a <meta http-equiv="refresh" content="5"> tag in the HTML header). But in this age of Web 2.0, that's simply not efficient enough. StockWatcher's users want their stock price updates and they want them now...without waiting for a full page refresh. In this section, you'll: A.Automatically refresh the Price and Change fields by implementing a timer and specifying a refresh rate. B.Encapsulate the stock price data by creating a class, StockPrice. C.Generate the stock data for the Price and Change fields by implementing the refreshWatchList method. D.Load the Price and Change fields with the stock data by implementing the updateTable method. E.Test the random generation of stock prices and change values

A. Automatically refresh stock data


GWT makes it easy to update an application's content on the fly. For StockWatcher, you'll automatically update stock prices by using the GWT Timer class. Timer is a single-threaded, browser-safe timer class. It enables you to schedule code to run at some point in the future, either once using the schedule() method or repeatedly using the scheduleRepeating() method. Because you want StockWatcher to automatically update the stock prices every five seconds, you'll use scheduleRepeating().

When a Timer fires, the run method executes. For StockWatcher you'll override the run method with a call to the refreshWatchList method which refreshes the Price and Change fields. For now, just put in a stub for the refreshWatchList method; later in this section, you'll implement it. 1.Implement the timer. Modify the onModuleLoad method to create a new instance of Timer, as follows:
public void onModuleLoad() {

...

// Move cursor focus to the text box. newSymbolTextBox.setFocus(true); // Setup timer to refresh list automatically. Timer refreshTimer = new Timer() { @Override public void run() { refreshWatchList(); } }; refreshTimer.scheduleRepeating(REFRESH_INTERVAL);

...

Eclipse flags Timer, REFRESH_INTERVAL and refreshWatchList. 2.Declare the import for Timer. If you are using Eclipse shortcuts, be sure to select the GWT Timer.
import com.google.gwt.user.client.Timer;

3.Specify the refresh rate. If you are using Eclipse shortcuts, select Create constant 'REFRESH_INTERVAL' then specify the refresh interval in milliseconds, 5000. Otherwise, just cut and paste from the highlighted code below.
public class StockWatcher implements EntryPoint {

private static final int REFRESH_INTERVAL = 5000; // ms private VerticalPanel mainPanel = new VerticalPanel();

4.Populate the price and change values as soon as a new stock is added. In the addStock method, replace the TODO comment with the highlighted code.
private void addStock() {

...

// Add a button to remove a stock from the table.

Button removeStockButton = new Button("x"); removeStockButton.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { int removedIndex = stocks.indexOf(symbol); stocks.remove(removedIndex); stocksFlexTable.removeRow(removedIndex + 1); } }); stocksFlexTable.setWidget(row, 3, removeStockButton);

// Get the stock price. refreshWatchList();

Eclipse flags refreshWatchList. 5.In the StockWatcher class, create a stub for the refreshWatchList method.
private void refreshWatchList() { // TODO Auto-generated method stub

B. Encapsulate stock price data

Creating a Java class using Eclipse


One of the primary ways GWT speeds AJAX development is by allowing you to write your applications in the Java language. Because of this, you can take advantage of static type checking and time-tested patterns of object-oriented programming. These, when combined with modern IDE features like code completion and automated refactoring, make it easier than ever to write robust AJAX applications with well-organized code bases. For StockWatcher, you'll take advantage of this capability by factoring stock price data into its own class. 1.Create a new Java class named StockPrice. In Eclipse, in the Package Explorer pane, select the packagecom.google.gwt.sample.stockwatcher.client From the Eclipse menu bar, select File > New > Class Eclipse opens a New Java Class window. 2.Fill in the New Class Window. At Name enter StockPrice Accept the defaults for the other fields. Press Finish 3.Eclipse creates stub code for the StockPrice class.
package com.google.gwt.sample.stockwatcher.client;

public class StockPrice { }

4.Replace the stub with following code.


package com.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; } }

C. Generate the stock data


Now that you have a StockPrice class to encapsulate stock price data, you can generate the actual data. To do this you'll implement the refreshWatchList method. Remember the refreshWatchList method is called both when the user adds a stock to the stock table, and then every 5 seconds when the timer fires.

Randomly generating the data


In lieu of retrieving real-time stock prices from an online data source, you'll create pseudo-random price and change values. To do this, use the GWT Random class. Then populate an array of StockPrice objects with these values and pass them on to the updateTable method. 1.Generate random stock prices. In the StockWatcher class, replace the stub refreshWatchList method with the following code.
/** * Generate random stock prices. */ 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[i] = new StockPrice(stocks.get(i), price, change); }

updateTable(prices); }

Eclipse flags Random and updateTable. 2.Include the import declaration.


import com.google.gwt.user.client.Random;

3.Create a stub for the updateTable(StockPrice[]) method.


private void updateTable(StockPrice[] prices) { // TODO Auto-generated method stub

D. Populate the Price and Change fields


Finally, load the randomly generated price and change data into the StockWatcher table. For each stock, format the Price and Change columns, then load the data. To do this you'll implement two methods in the StockWatcher class. Format the values in the Price field to two decimal places (1,956.00). Prefix the values in the Change field with a sign (+/-). 1.Implement the method updateTable(StockPrices[]). Replace the stub with the following code.
/** * Update the Price and Change fields all the rows in the stock table. * * @param prices Stock data for all rows. */ private void updateTable(StockPrice[] prices) { for (int i = 0; i < prices.length; i++) { updateTable(prices[i]); }

Eclipse flags updateTable. Create a stub for the updateTable(StockPrice) method. 2.Implement the method updateTable(StockPrice). Replace the stub with the following code.
/** * Update a single row in the stock table. * * @param price Stock data for a single row. */ private void updateTable(StockPrice price) { // Make sure the stock is still in the stock table. if (!stocks.contains(price.getSymbol())) { return; }

int row = stocks.indexOf(price.getSymbol()) + 1;

// Format the data in the Price and Change fields. 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());

// Populate the Price and Change fields with new data. stocksFlexTable.setText(row, 1, priceText); stocksFlexTable.setText(row, 2, changeText + " (" + changePercentText + "%)"); }

Eclipse flags NumberFormat. 3.Include the import declaration.


import com.google.gwt.i18n.client.NumberFormat;

E. Test the random generation of stock prices and change values


At this point, the Price and Change fields should be populated with the stock data you randomly generated. Try it and see. 1.Run StockWatcher in development mode. 2.Add a stock. The Price and Change fields should have data. Every 5 seconds, the data should refresh.

3. Adding the timestamp


The final piece of functionality you need to implement is the timestamp. You used a Label widget, lastUpdatedLabel, to create the timestamp in the UI. Now set the text for the Label widget. Add this code to the updateTable(StockPrice[]) method. A.Implement the timestamp. In the updateTable(StockPrice[]) method, copy and paste the highlighted code.
/** * Update the Price and Change fields all the rows in the stock table. * * @param prices Stock data for all rows. */ private void updateTable(StockPrice[] prices) { for (int i = 0; i < prices.length; i++) { updateTable(prices[i]); }

// Display timestamp showing last refresh. lastUpdatedLabel.setText("Last update : " + DateTimeFormat.getMediumDateTimeFormat().format(new Date()));

Eclipse flags DateTimeFormat and Date. B.Include the imports.


import com.google.gwt.i18n.client.DateTimeFormat; import java.util.Date;

C.Test the timestamp. Save your changes. In the browser, press Refresh to load the changes. The timestamp label should be displayed beneath the stock table. As the Price and Change fields refresh, the timestamp should display the date and time of the last update. Implementation Note: You may have noticed that the classes DateTimeFormat and NumberFormat live in a subpackage of com.google.gwt.i18n, which suggest that they deal with internationalization in some way. And indeed they do: both classes will automatically use your application's locale setting when formatting numbers and dates. You'll learn more about localizing and translating your GWT application into other languages in the tutorial Internationalizing a GWT Application.

What's Next
At this point you've built the interface components and coded all the underlying client-side functionality for the StockWatcher application. The user can add and remove stocks. The Price and Change fields update every 5 seconds. A timestamp shows when the last refresh occurred.

Although you have not compiled StockWatcher yet, you can test it in production mode here: Run StockWatcher

A Bug
For the sake of this tutorial, we introduced an error into the code. Can you detect it? Look at the change percentages. Don't they seem a bit small? If you do the math, you'll discover that they appear to be exactly an order of magnitude smaller than they should be. There's an arithmetic error hiding somewhere in the StockWatcher code. Using the tools provided by GWT and your Java IDE, your next step is to find and fix the error. Step 6: Debugging a GWT Application

Step 6: Debug a GWT Application


At this point, you've finished implementing the StockWatcher UI and all its client-side functionality. However, you've noticed that there is an error in the Change field. The percentage of change is not calculating correctly. In this section, you'll use Eclipse to debug your Java code while running StockWatcher in development mode.

1.Find the bug. 2.Fix the bug. 3.Test the bug fix by running StockWatcher in development mode. Benefits
You can debug the Java source code before you compile it into JavaScript. This GWT development process help you take advantage of the debugging tools in your Java IDE. You can: Set break points. Step through the code line by line. Drill down in the code. Inspect the values of variables. Display the stack frame for suspended threads. One of attractions of developing in JavaScript is that you can make changes and see them immediately by refreshing the browser without having to do a slow compilation step. GWT development mode provides the exact same development cycle. You do not have to recompile for every change you make; that's the whole point of development mode. Just click "Refresh" to see your updated Java code in action.

1. Finding the bug


Analyzing the problem

Looking at the values in the Price and Change fields, you can see that, for some reason, all of the change percentages are only 1/10 the size of the correct values. The values for the Change field are loaded by the updateTable(StockPrice) method.
/** * Update a single row in the stock table. * * @param price Stock data for a single row. */

private void updateTable(StockPrice price) { // Make sure the stock is still in the stock table. if (!stocks.contains(price.getSymbol())) { return; }

int row = stocks.indexOf(price.getSymbol()) + 1;

// Format the data in the Price and Change fields. 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());

// Populate the Price and Change fields with new data. stocksFlexTable.setText(row, 1, priceText); stocksFlexTable.setText(row, 2, changeText + " (" + changePercentText + "%)"); }

Just glancing at the code, you can see that the value of the changePercentText variable is being set elsewhere, in price.getChangePercent. So, first set a breakpoint on that line and then drill down to determine where the error in calculating the change percentage is.

Setting break points


1.Set a breakpoint on the lines of code you want to step into and where you want to examine variable values. In StockWatcher.java, in the updateTable(StockPrice price) method, set a breakpoints on these two lines
String changePercentText = changeFormat.format(price.getChangePercent()); stocksFlexTable.setText(row, 1, priceText);

Eclipse switches to Debug perspective. 2.Run the code that has the error. To run the code in the updateTable method where you suspect the error, just add a stock to the stock list in the browser running in development mode. Execution will stop at the first breakpoint. 3.Check the values of the variables priceText and changeText. In the Eclipse Debug perspective, look at the Variables pane. 4.Run the code to the next break point, where priceText is set. In the Debug pane, press the Resume icon. 5.Check the values of the variables priceText, changeText, changePercentText. In the Eclipse Debug perspective, look at the Variables pane. If you like, double-check the math to see the error.

6.Loop back to the first break point, where changePercentText is set. In the Debug pane, press the Resume icon.

Stepping through the code


Now step into the code to see where and how the changePercentText is being calculated. 1.Step into the getChangePercent method to see how it's calculating the value of changePercentText.
public double getChangePercent() { return 10.0 * this.change / this.price; }

Looking at the getChangePercent method, you can see the problem: it's multiplying the change percentage by 10 instead of 100. That corresponds exactly with the output you saw before: all of the change percentages were only 1/10 the size of the correct values.

2. Fixing the bug


1.Fix the error in calculating the percentage of the price change. In StockPrice.java, edit the getChangePercent method.
public double getChangePercent() { return 100.0 * this.change / this.price; }

Tip: In Eclipse, if you find it easier to edit in the Java perspective rather than the Debug perspective, you can switch back and forth while running StockWatcher in development mode.

3. Testing the bug fix in development mode


At this point when you enter a stock code, the calculation of the Change field should be accurate. Try it and see. 1.In Eclipse, toggle all the breakpoints off and press Resume. 2.In the browser running in development mode, press Refresh. 3.Add a stock. 4.Check the calculation of the value in the Change field.

What's Next
At this point you've implemented all your functional requirements. StockWatcher is running and you've found and fixed a bug. Now you're ready to enhance StockWatcher's visual design. You'll apply CSS style rules to the GWT widgets and add a static element (a logo) to the page. Step 7: Applying Style

Step 7: Apply Style


At this point, StockWatcher is functional. Now you want to give it some visual style.

In this section, you'll:

1.Associate style sheets with the project. 2.Change the theme. 3.Create a secondary style. 4.Create a dependent secondary style. 5.Update styles dynamically. 6.Set an element's HTML attributes 7.Add images or other static HTML elements.

Benefits of CSS
GWT provides very few Java methods directly related to style. Rather, we encourage you to define styles in Cascading Style Sheets. When it comes to styling web applications, CSS is ideal. In addition to cleanly separating style from application logic, this division of labor helps applications load and render more quickly, consume less memory, and even makes them easier to tweak during edit/debug cycles because there's no need to recompile for style tweaks.

1. Associating Style Sheets with a Project


Two style sheets are already associated with the StockWatcher project. a theme style sheet, standard.css: where the GWT default styles are defined the application style sheet, StockWatcher.css: where you define the specific styles for StockWatcher When you used webAppCreator to create StockWatcher, it created the application style sheet (StockWatcher.css). It also referenced the theme in the GWT module. 1.Open the GWT module, StockWatcher/src/com/google/gwt/sample/stockwatcher/StockWatcher.gwt.xml. Notice that the Standard theme is being used by default.

<?xml version="1.0" encoding="UTF-8"?> <module rename-to='stockwatcher'> <!-- Inherit the core Web Toolkit stuff. <inherits name='com.google.gwt.user.User'/> -->

<!-- Inherit the default GWT style sheet.

You can change

--> --> -->

<!-- the theme of your GWT application by uncommenting <!-- any one of the following lines. <inherits name='com.google.gwt.user.theme.standard.Standard'/>

<!-- <inherits name="com.google.gwt.user.theme.chrome.Chrome"/> --> <!-- <inherits name="com.google.gwt.user.theme.dark.Dark"/> -->

<!-- Other module inherits

-->

<!-- Specify the app entry point class.

-->

<entry-point class='com.google.gwt.sample.stockwatcher.client.StockWatcher'/> </module>

2.Open StockWatcher/war/StockWatcher.html. Notice that the application style sheet is StockWatcher.css.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8">

<link type="text/css" rel="stylesheet" href="StockWatcher.css">

<title>StockWatcher</title>

<script type="text/javascript" language="javascript" src="stockwatcher/stockwatcher.nocache.js"></script> </head>

<body>

<h1>StockWatcher</h1>

<div id="stockList"></div>

<iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>

</body> </html>

Automatic Resource Inclusion


Like images, CSS files are static resources that are stored in the public directory and referenced from the HTML host page. You can associate a style sheet with your application either of two ways. Preferred: in the module XML file (StockWatcher.gwt.xml) Alternate: in the HTML host page (StockWatcher.html) Whichever method you choose, you can associate one or more application style sheets with your project. They cascade in the order they are listed, just as they do in an HTML document. For StockWatcher, you'll follow the preferred strategy. Rather than put links to the style sheets in the HTML host page, you'll use the module XML file. Then, when you compile StockWatcher, the GWT compiler will bundle all the static resources required to run your application including the style sheets. This mechanism is called Automatic Resource Inclusion. In most cases, it is the better strategy because the style sheet will follow your module wherever it is used in new contexts, no matter what HTML host page you embed it in. As you get into more complex development, you will want to reuse or share modules. Shared modules do not include a host page and therefore, you cannot guarantee the availability of the application style sheet unless you use Automatic Resource Inclusion. If you have a case where you want whatever host page your module is embedded in to dictate the styles for its widgets, then don't include the style sheet in the module XML file.

2. Changing the Theme


GWT ships with three themes: Standard, Chrome, and Dark. The Standard theme is selected by default when the GWT module is created. Each application can use only one theme at a time. However, if you have an existing style or you prefer to design one from scratch, you don't have to use any theme at all. Take a moment to see what the other themes look like. Change the theme from Standard to Dark. 1.In StockWatcher.gwt.xml, comment out the line referencing the Standard theme. Eclipse shortcut: Source > Toggle Comment. 2.Uncomment the line referencing the Dark theme. Eclipse shortcut: Source > Toggle Comment.
<!-- Inherit the default GWT style sheet. You can change --> --> --> <!-- the theme of your GWT application by uncommenting <!-- any one of the following lines. <!-- <inherits name='com.google.gwt.user.theme.standard.Standard'/> --> <!-- <inherits name="com.google.gwt.user.theme.chrome.Chrome"/> --> <inherits name="com.google.gwt.user.theme.dark.Dark"/>

3.Test the change. Save your changes to the StockWatcher.gwt.xml file. If development mode is still running, terminate it and then relaunch StockWatcher. For StockWatcher, you are going to build on the Standard theme. So after you've played around to see how themes work, set the theme back to Standard. Note: GWT themes also come in RTL (right-to-left) versions to support languages written from right-to-left, such as Arabic. To use a right-to-left theme, append RTL to the theme name. <inherits
name='com.google.gwt.user.theme.standard.StandardRTL'/>

Deciding on Implementation Strategies


There are various ways you can modify GWT's default styles. You can create new style rules in the application style sheet, Stockwatcher.css.

Changes made with this approach apply to all widgets of a type, for example, to all Buttons. You can append a secondary style to the HTML element by adding another class attribute. Changes made with this approach apply to a single widget, for example just the Remove button in StockWatcher. You can override a widget's primary style name. If you need to clear any existing styles that you are inheriting from the theme or other style sheets, you can change the default primary class associated with the widget. If you are embedding a GWT application into an existing page, you can edit your own style sheet. You can start completely from scratch. For the StockWatcher application, you'll focus mostly on the second approach: you'll learn to append a secondary style.

3. Associating style rules with GWT-generated HTML elements


An Element's Primary Style
You might have noticed the buttons for StockWatcher have a gradient background. Where is the button style coming from? If, after you compile StockWatcher, you look at the generated JavaScript for the Add button, you will see that the button has a class attribute of gwt-Button:
<button class="gwt-Button" tabindex="0" type="button">Add</button>

In GWT, each class of widget has an associated style name (like gwt-Button) that binds it to a CSS style rule. This is the widget's primary style. Default values are defined for the primary style in the theme style sheet. Type of Element Buttons in static HTML and GWT-generated buttons Only GWT-generated buttons Only my special GWT-generated button HTML Tag <button> <button class="gwt-Button"> <button class="gwt-Button mybutton"> CSS Selector button button.gwtButton button.mybutton

Tip: You can look up the name of the style rule (the CSS selector) for each widget by accessing the GWT API Reference via the Widget Gallery. GWT takes advantage of the fact that you can associate multiple classes with an HTML element so that you can specify a style for a specific GWT-generated element and not affect others of the same type. In this section, you'll learn how to set the secondary class on a GWT-generated HTML element.

Creating a Secondary Style


Creating a secondary style for an HTML element is a two-step process: 1.Specify the style rule in StockWatcher.css. 2.Apply the style by setting HTML class attributes programmatically in StockWatcher.java. Let's make one quick change to see how the mechanism works. Then you can make the rest of the changes in one pass. We'll start by changing the colors of the first row where we've stored header information.

Defining the Style in CSS


When you created the StockWatcher application, webAppCreator generated the application style sheet (StockWatcher.css) and added a pointer to it in the module XML file (StockWatcher.gwt.xml). So, you're ready to start defining style rules. 1.Open the application style sheet. Open StockWatcher/war/StockWatcher.css. 2.For any HTML element with the class attribute of watchListHeader, set the color and text properties.

Replace the contents of StockWatcher.css with the following style rules.


/* Formatting specific to the StockWatcher application */ body { padding: 10px; } /* stock list header row */ .watchListHeader { background-color: #2062B8; color: white; font-style: italic; }

3.Save your changes to StockWatcher.css.

Applying the style with the addStyleName method


In a web page with static elements, you would now go through the HTML source and add class attributes to various elements to associate them with the styles defined in the CSS file. For example:
<tr class="watchListHeader">

However, GWT elements are created dynamically at runtime. So you'll set the HTML class attributes in the Java source using the addStyleName method. You'll specify the row (the header is row 0) and the name of the secondary class, watchListHeader. 1.In StockWatcher.java, in the onModuleLoad method, add a secondary style to the header row in the stock table.
public void onModuleLoad() { // Create table for stock data. stocksFlexTable.setText(0, 0, "Symbol"); stocksFlexTable.setText(0, 1, "Price"); stocksFlexTable.setText(0, 2, "Change"); stocksFlexTable.setText(0, 3, "Remove");

// Add styles to elements in the stock list table. stocksFlexTable.getRowFormatter().addStyleName(0, "watchListHeader");

2.Save your changes to StockWatcher.java and then press Refresh in the browser running development mode to see them. The the header row in the flex table displays white italic headings against a blue background.

Implementing the Remaining Secondary Style Changes for StockWatcher


Each of the changes we will make by creating a secondary style is outlined below. You can apply them one at a time to see the effect of each change or you can copy and paste the set of changes summarized at the end of this section. Put a border around the stock list. Right-align numeric data in the stock list. Center the Remove buttons and make them wider. Add whitespace (padding) to the Add Stock panel.

Put a border around the stock list


1.Define the style. In StockWatcher.css, create a style rule for HTML elements with a class attribute of watchList.
/* stock list header row */ .watchListHeader { background-color: #2062B8; color: white; font-style: italic; }

/* stock list flex table */ .watchList { border: 1px solid silver; padding: 2px; margin-bottom:6px; }

2.Apply the style. In StockWatcher.java, add a secondary class attribute to the stock flex table.
// Add styles to elements in the stock list table. stocksFlexTable.getRowFormatter().addStyleName(0, "watchListHeader"); stocksFlexTable.addStyleName("watchList");

3.Save your changes and then press Refresh in the browser running development mode to see them. The stock list table has a silver border.

Right-align numeric data in the stock list


First, you'll format the text in the stock table's header row, which loads when StockWatcher is launched. Later, you'll apply the same style rules to the table rows that contain the stock data. 1.Define the style. In StockWatcher.css, create a style rule that will right-align the columns that contain the Price and Change fields.
/* stock list flex table */ .watchList { border: 1px solid silver; padding: 2px; margin-bottom:6px; }

/* stock list Price and Change fields */ .watchListNumericColumn { text-align: right; width:8em; }

2.Apply the style. In StockWatcher.java, add a secondary class attribute to both the Price and Change fields.

// Add styles to elements in the stock list table. stocksFlexTable.getRowFormatter().addStyleName(0, "watchListHeader"); stocksFlexTable.addStyleName("watchList"); stocksFlexTable.getCellFormatter().addStyleName(0, 1, "watchListNumericColumn"); stocksFlexTable.getCellFormatter().addStyleName(0, 2, "watchListNumericColumn");

3.Save your changes and then press Refresh in the browser running development mode to see them. The Price and Change columns have a set width and the text in the header row is right-aligned.

Center the Remove buttons and make them wider


1.Define the style. In StockWatcher.css, create a style rule that will center the text in the column that contains the Remove button.
/* stock list Price and Change fields */ .watchListNumericColumn { text-align: right; width:8em; }

/* stock list Remove column */ .watchListRemoveColumn { text-align: center; }

2.Apply the style. In StockWatcher.java, add a secondary class attribute to the Remove field.
// Add styles to elements in the stock list table. stocksFlexTable.getRowFormatter().addStyleName(0, "watchListHeader"); stocksFlexTable.addStyleName("watchList"); stocksFlexTable.getCellFormatter().addStyleName(0, 1, "watchListNumericColumn"); stocksFlexTable.getCellFormatter().addStyleName(0, 2, "watchListNumericColumn"); stocksFlexTable.getCellFormatter().addStyleName(0, 3, "watchListRemoveColumn");

3.Save your changes and then press Refresh in the browser running development mode to see them. The caption takes up the entire width of the field. You'll be able to see that the buttons are centered in the Remove column after you format the data rows in the next step.

Apply the same cell formatting to the rows that hold the stock data
You've formatted the header row of the flex table, which is displayed when StockWatcher starts up. Remember, however, that in a flex table, the rows holding the stocks aren't created until the user adds a stock to the list. Therefore, you will add the code for formatting the stock data in the addStock method rather than in the onLoad method. 1.You have already defined the style in StockWatcher.css. 2.Apply the style. In StockWatcher.java, in the addStock method, add secondary class attribute to the table cells in the Price, Change, and Remove columns.
// Add the stock to the table. int row = stocksFlexTable.getRowCount();

stocks.add(symbol); stocksFlexTable.setText(row, 0, symbol); stocksFlexTable.getCellFormatter().addStyleName(row, 1, "watchListNumericColumn"); stocksFlexTable.getCellFormatter().addStyleName(row, 2, "watchListNumericColumn"); stocksFlexTable.getCellFormatter().addStyleName(row, 3, "watchListRemoveColumn");

3.Save your changes and then press Refresh in the browser running development mode to see them. Add stocks to the list. The Price and Change data is right-aligned. The Remove button is centered.

Add margins around the Add Stock panel


Add whitespace around the text box and Add button in the Add Stock panel. 1.Define the style. In StockWatcher.css, create a style rule to widen the margins around the Add Stock panel.
/* stock list Remove column */ .watchListRemoveColumn { text-align: center; }

/* Add Stock panel */ .addPanel { margin: 10px 0px 15px 0px; }

2.Apply the style. In StockWatcher.java, in the onModuleLoad method add a secondary class attribute to the addPanel.
// Assemble Add Stock panel. addPanel.add(newSymbolTextBox); addPanel.add(addStockButton); addPanel.addStyleName("addPanel");

3.Save your changes and then press Refresh in the browser running development mode to see them. The margin between the stock table and the Add Stock panel has increased.

Summary of Changes
Here the summary of the changes we've done so far.

Changes to StockWatcher.css
/* Formatting specific to the StockWatcher application */

body { padding: 10px; }

/* stock list header row */ .watchListHeader { background-color: #2062B8; color: white; font-style: italic; }

/* stock list flex table */ .watchList { border: 1px solid silver; padding: 2px; margin-bottom:6px; }

/* stock list Price and Change fields */ .watchListNumericColumn { text-align: right; width:8em; }

/* stock list Remove column */ .watchListRemoveColumn { text-align: center; }

/* Add Stock panel */ .addPanel { margin: 10px 0px 15px 0px; }

Changes to StockWatcher.java, onModuleLoad


public void onModuleLoad() { // Create table for stock data. stocksFlexTable.setText(0, 0, "Symbol"); stocksFlexTable.setText(0, 1, "Price"); stocksFlexTable.setText(0, 2, "Change"); stocksFlexTable.setText(0, 3, "Remove");

// Add styles to elements in the stock list table. stocksFlexTable.getRowFormatter().addStyleName(0, "watchListHeader"); stocksFlexTable.addStyleName("watchList"); stocksFlexTable.getCellFormatter().addStyleName(0, 1, "watchListNumericColumn"); stocksFlexTable.getCellFormatter().addStyleName(0, 2, "watchListNumericColumn"); stocksFlexTable.getCellFormatter().addStyleName(0, 3, "watchListRemoveColumn");

// Assemble the Add Stock panel addPanel.add(newSymbolTextBox); addPanel.add(addStockButton); addPanel.addStyleName("addPanel"); . . . }

Changes to StockWatcher.java, addStock


// Add the stock to the table. int row = stocksFlexTable.getRowCount(); stocks.add(symbol); stocksFlexTable.setText(row, 0, symbol); stocksFlexTable.getCellFormatter().addStyleName(row, 1, "watchListNumericColumn");

stocksFlexTable.getCellFormatter().addStyleName(row, 2, "watchListNumericColumn"); stocksFlexTable.getCellFormatter().addStyleName(row, 3, "watchListRemoveColumn");

4. Creating secondary styles dependent on a primary style


Next you want to change the style of the Remove button. The Remove button inherits its style from the Button widget. Default styles for all GWT Button widgets are defined by GWT in standard.css.
<button class="gwt-Button" tabindex="0" type="button">x</button> .gwt-Button { background:transparent url(images/hborder.png) repeat-x scroll 0px -27px; border:1px outset #CCCCCC; cursor:pointer; font-size:small; margin:0pt; padding:3px 5px; text-decoration:none; }

For StockWatcher, you want your style change to apply only to the Remove button. So you'll do just as you've been doing: add a secondary style to the Remove button element. But this time, you'll make the secondary style dependent on the primary style. Dependent styles are powerful because they are automatically updated whenever the primary style name changes. In contrast, secondary style names that are not dependent style names are not automatically updated when the primary style name changes. To do this, you'll use the addStyleDependentName method instead of the addStyleName method. 1.Define the style rule.
/* Add Stock panel */ .addPanel { margin: 10px 0px 15px 0px; }

/* stock list, the Remove button */ .gwt-Button-remove { width: 50px; }

2.Apply the style. In StockWatcher.java, use addStyleDependentName to add a secondary, dependent class attribute to the Remove button.
// Add a button to remove this stock from the table. Button removeStockButton = new Button("x"); removeStockButton.addStyleDependentName("remove");

3.Save your changes and then press Refresh in the browser running development mode to see them. The Remove button is wider than it is tall. The Add button is unaffected by this change. 4.Now the resulting generated HTML has two class attributes.
<button class="gwt-Button gwt-Button-remove" tabindex="0" type="button">x</button>

5. Updating styles dynamically


The final style change you want to implement is changing the color of the price change. If the stock price goes up, StockWatcher displays it in green; down, in red; no change, in black. This is the one style that changes dynamically as StockWatcher runs. You've already applied an HTML class attribute to the cell element to right-align the numeric values inside the cells. To keep the code simple, it would be nice if you could apply the HTML class attributes just to the text inside the cell. An easy way to do this would be to use a nested widget. In this case, you'll insert a Label widget inside every cell inside column 2. 1.Define the style. In StockWatcher.css, add these style rules.
/* stock list, the Remove button */ .gwt-Button-remove { width: 50px; }

/* Dynamic color changes for the Change field */ .noChange { color: black; } .positiveChange { color: green; } .negativeChange { color: red; }

2.Insert a Label widget in a table cell. In StockWatcher.java, in the addStock method, create a Label widget for every cell in column 2.
// Add the stock to the table. int row = stocksFlexTable.getRowCount(); stocks.add(symbol); stocksFlexTable.setText(row, 0, symbol); stocksFlexTable.setWidget(row, 2, new Label()); stocksFlexTable.getCellFormatter().addStyleName(row, 1, "watchListNumericColumn"); stocksFlexTable.getCellFormatter().addStyleName(row, 2, "watchListNumericColumn"); stocksFlexTable.getCellFormatter().addStyleName(row, 3, "watchListRemoveColumn");

Instead of setting the text on the table cells, now you have to set the text for the Label widget. 3.Set text on the changeWidget. In the updateTable(StockPrice) method, delete the call to setText for the Change column (column 2). Create an instance of the Label widget and call it changeWidget. Set the text on changeWidget.
// Populate the Price and Change fields with new data. stocksFlexTable.setText(row, 1, priceText); stocksFlexTable.setText(row, 2, changeText + " (" + changePercentText

+ "%)"); Label changeWidget = (Label)stocksFlexTable.getWidget(row, 2); changeWidget.setText(changeText + " (" + changePercentText + "%)");

4.Change the color of each changeWidget based on its value.


// Populate the Price and Change fields with new data. stocksFlexTable.setText(row, 1, priceText); Label changeWidget = (Label)stocksFlexTable.getWidget(row, 2); changeWidget.setText(changeText + " (" + changePercentText + "%)");

// Change the color of text in the Change field based on its value. String changeStyleName = "noChange"; if (price.getChangePercent() < -0.1f) { changeStyleName = "negativeChange"; } else if (price.getChangePercent() > 0.1f) { changeStyleName = "positiveChange"; }

changeWidget.setStyleName(changeStyleName);

5.Save your changes and then press Refresh in the browser running development mode to see them. The color of the values in the Change field are red, green, or black depending on whether the change was negative, positive, or none.

6. Setting an element's HTML attributes


Occasionally, you do want to set style attributes directly on an HTML element rather than define a style rule in CSS. For example, the HTML table element has a cellpadding attribute that is convenient for setting the padding on all the cells in the table. In GWT, depending on the HTML element, you can set some attributes in the Java code to generate the appropriate HTML. 1.Specify the cellpadding for the stock table. In StockWatcher.java, in the onModuleLoad method, add the setCellPadding method.
public void onModuleLoad() { // Create table for stock data. stocksFlexTable.setText(0, 0, "Symbol"); stocksFlexTable.setText(0, 1, "Price"); stocksFlexTable.setText(0, 2, "Change"); stocksFlexTable.setText(0, 3, "Remove");

// Add styles to elements in the stock list table. stocksFlexTable.setCellPadding(6);

2.Save your changes and then press Refresh in the browser running development mode to see them.

7. Adding images or other static HTML elements


Your application's HTML host page can include whatever additional static HTML elements you require. For example, in StockWatcher, you'll add the Google Code logo. To include images, put them in the project's public directory. The GWT compiler will copy all the necessary files to the output directory for deployment.

To include static images in the application. 1.Create an directory to hold the image files associated with this application. In the war directory, create an images directory.
StockWatcher/war/images

2.From this page, copy the image of the logo and paste it into the images directory.
StockWatcher/war/images/GoogleCode.png

3.In StockWatcher.html, insert an img tag pointing to the logo file.


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8">

<link type="text/css" rel="stylesheet" href="StockWatcher.css">

<title>StockWatcher</title>

<script type="text/javascript" language="javascript" src="stockwatcher/stockwatcher.nocache.js"></script> </head>

<body>

<img src="images/GoogleCode.png" />

<h1>StockWatcher</h1>

<div id="stockList"></div>

<iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>

</body> </html>

Note: HTML comments have been omitted for brevity. 4.Save your changes and then press Refresh in the browser running development mode to see them.

In Depth: For more information on including style sheets, JavaScript files, and other GWT modules, see the Developer's Guide, Automatic Resource Inclusion.

What's Next
At this point you've finished with the initial implementation of StockWatcher. The client-side functionality is working and the user interface has a new visual design.

Note: For the sake of simplicity, we created the user interface for this tutorial programmatically using widgets. This works fine for StockWatcher because the UI is fairly simple. However, GWT has a powerful tool called UiBinder that allows you to create complex interfaces using declarative XML files, which can reduce code size and complexity. Check out Developer's Guide sections on Declarative Layout with UiBinder and Build User Interfaces for more info about UiBinder and UI design in general. Now you're ready to compile StockWatcher. You'll compile your Java code into JavaScript and check that StockWatcher runs the same way in production mode as it has in development mode. Step 8: Compiling a GWT Application

Step 8: Compiling a GWT Application


At this point, your initial implementation of StockWatcher is complete. So far, you've been running StockWatcher in development mode. In development mode you can see the effect of your code changes immediately and use your IDE's debugging tools. After you compile StockWatcher, you can run and test it in production mode. When an application runs in production mode, it exists as pure JavaScript but does not require any browser plugins or the Java Virtual Machine (JVM). In this section, you'll:

1.Compile the Java source code. 2.Test StockWatcher in production mode. 3.Deploy StockWatcher to a web server.
You'll also learn about deferred binding, GWT's mechanism for serving just the code required depending on browser or, optionally, other factors such as locale.

1. Compiling Java to JavaScript


To compile the Java source code to JavaScript, you'll use the GWT compiler.

Compiling the StockWatcher application (using Eclipse)


1.In the Package Explorer view, select the StockWatcher project. 2.In the toolbar, click the GWT Compile Project button . 3.Confirm the compiler options and click the Compile button. In the Eclipse console you will see the output of the GWT compiler, for example,
Compiling module com.google.gwt.sample.stockwatcher.StockWatcher Compiling 6 permutations Compiling permutation 0... Compiling permutation 1... Compiling permutation 2... Compiling permutation 3... Compiling permutation 4... Compiling permutation 5... Compile of permutations succeeded Linking into war/stockwatcher. Link succeeded Compilation succeeded -- 30.504s

Compiling the StockWatcher application (without using Eclipse)


You can compile the StockWatcher application directly from the command line: 1.Change to the StockWatcher directory. 2.Execute: ant build If you run the GWT compiler from the command line, you can specify the style of the JavaScript, the level of logging detail, and override other default behaviors by modifying the StockWatcher/build.xml file.

2. Testing in Production Mode


After the application is compiled, you can run it in production mode by opening StockWatcher.html in a new browser window. StockWatcher looks and behaves just as it did in development mode. The real difference is hidden under the covers. When you interact with StockWatcher now, it's executing JavaScript code in the browser, not Java bytecode in the JVM. Tip: If you launched the development mode server, you can run your application in production mode (after compiling it) by removing the gwt.codesvr parameter from the URL before loading the application.

3. Deploying the Application to a Web Server


At this point, you could deploy StockWatcher to a public web server simply by uploading the files in the output directory. This initial version does not need to communicate with the server in any way; therefore, it does not require anything special on the part of the web server. Any server that can serve up static web pages will do just fine.

Compiler Output
Take a look at the files generated by the GWT compiler. In the output directory StockWatcher/war/stockwatcher, you see a set of files similar to this:
14A43CD7E24B0A0136C2B8B20D6DF3C0.cache.png 29F4EA1240F157649C12466F01F46F60.gwt.rpc 34BCFF35CB8FD43BCBFC447B883BCADC.cache.html 52BE5EB1FD9F0C2659714EE874E24999.cache.html 548CDF11D6FE9011F3447CA200D7FB7F.cache.png 667F52D77BB3D008A2A6A484569C3C35.cache.html 9DA92932034707C17CFF15F95086D53F.cache.png A7CD51F9E5A7DED5F85AD1D82BA67A8A.cache.png B8517E9C2E38AA39AB7C0051564224D3.cache.png CC9E2ADC408F47959407F6440C301B88.cache.html DF6EEF5BB835EE6FD9BBA5E092B0C429.cache.html clear.cache.gif gwt hosted.html stockwatcher.nocache.js

In addition to the static resources in StockWatcher/war (such as the HTML host page, style sheet, and images directory), notice the other file names contain GUIDs. These files contain the various JavaScript implementations of StockWatcher. GWT generates multiple implementations of your application, a unique permutation for each supported web browser.

Optimizing Runtime with Deferred Binding


At runtime, GWT uses a mechanism called deferred binding to load the correct permutation for the end user's browser. Deferred binding serves just the code the user needs and no more. What are the benefits of deferred binding? Because each permutation is tailored to work around the bugs and idiosyncrasies of its intended web browser, using deferred binding is Faster for the user. Your application download contains no unnecessary bytes. The application doesn't need to sniff for browsers or provide multiple branches for each browser. Faster for you. GWT does the work of generating the correct JavaScript for each browser so that you don't have to spend so much time dealing with differences between browsers. In addition to browser detection, deferred binding can also generate customized versions of your application for any number of other variables. One very common example is internationalization. With deferred binding, GWT generates a separate implementation of the application for each language, so for example, an English speaker doesn't have to download the French text (and vice versa). You can try this for yourself in the tutorial Internationalizing a GWT Application.

What's Next?
At this point you've tested StockWatcher both in development mode and in production mode. By now you should have a pretty good idea of how to develop a GWT application, which has only client-side functionality, from start to finish. To build upon the initial implementation of StockWatcher and learn additional features of GWT, select from the following tutorials:

Internationalizing a GWT application Communicating with the server via GWT RPC. Retrieving JSON data via HTTP

Unit testing with JUnit Running a GWT application on Google App Engine

Das könnte Ihnen auch gefallen