Step 3: ユーザインターフェースの構築

目次:Google Web Toolkit チュートリアル(目次) - webとかmacとかやってみようか
原文:http://code.google.com/intl/ja/webtoolkit/doc/1.6/tutorial/buildui.html

ここまでで、StockWatcherプロジェクトの作成と、仕様の確認をしてきたよね。このセクションでは、GWTウィジェットとパネルを使ったユーザインターフェースの構築をするよ。

  1. UI要素を実装するのに必要なGWTウィジェットの選択
  2. UI要素を配置するのに必要なGWTパネルの選択
  3. ホストページ StockWatcher.html に アプリケーションの埋め込み
  4. StockWatcher.javaウィジェットとパネルの実装
  5. ホストモードでレイアウトのテスト

GWTクロスブラウザ互換の心配からあなたを保護するよ。GWTウィジェットでインターフェースを構築すると、最新の FireFoxIEOperaSafari でアプリケーションは同じように動くでしょう。しかし、DHTMLは非常に癖があるので、全てのブラウザでテストしないといけないよ。

1. UI要素を実装するのに必要なGWTウィジェットの選択

最初に、 Widget Gallery を見て、それぞれのUI要素のGWTウィジェットを選択するよ。

ウィジェットギャラリーのウィジェットはデフォルトのスタイルなので、最終的な StockWatcherのように見えるわけではないよ。今は心配はいらないよ。最初にウィジェットの動きを見て、それからCSSを使って見た目を変えるよ。

株データテーブル

GWT は FlexTable と呼ばれる特別なテーブルを提供しているよ。FlexTableウィジェットは要求毎にセルを作成する。ユーザが株データをいくつ追加しているか分からないので、これはまさに株データを含むテーブルに必要としているよ。FlexTableウィジェットで実装されたテーブルは、ユーザが株を追加、削除すると伸び縮みするよ。

ボタン

可能であればいつでも、GWTはブラウザ固有のユーザインターフェース要素を尊重するよ。例えば、Buttonウィジェットは、<div>要素などで作られたボタンの様なウィジェットではなく、まさに HTML の <button&gh; になるよ。これは、GWTのボタンは別のブラウザ、別のOSでちょうどよく描画されることを意味しているよ。ブラウザネイティブの制御を使うメリットは、高速で、利用しやすく、ユーザにとって最も馴染みがあることだよ。また、CSSでスタイルもできるよ。

インプットボックス

GWTは様々な入力フィールドを提供するよ。

  • TextBox ウィジェット: 一行のテキストボックス
  • PassWordTextBox ウィジェット: 入力をマスクするテキストボックス
  • TextArea ウィジェット: 複数行のテキストボックス
  • SuggestBox: 予め設定されたアイテムを表示

StockWatcher では、ユーザは 株コードを一行で入力するので、TextBox ウィジェットを実装するよ。

ラベル

Buttonウィジェットと比較すると、Label ウィジェットは HTMLフォームで使われる<label>要素に結びつけられていない。代わりに、HTMLで解釈されない、任意のテキストを含む<div>要素に結びつけられるよ。これはインライン要素ではなくブロック要素になるよ。

<div class="gwt-Label">Last update : Oct 1, 2008 1:31:48 PM</div>

もし、StockWatcherインターフェースの構築に使うGWTウィジェットAPIリファレンスを覗いてみたければ、下のテーブルのリンクをクリックしてね。

UI要素 GWT実装
テーブル:株データを保持 FlexTable ウィジェット
二つのボタン:追加/削除ボタン Button ウィジェット
入力ボックス:証券コードを入力 TextBox ウィジェット
タイムスタンプ: 最終更新日時を表示 Label ウィジェット
ロゴ HTMLホストページで参照される画像ファイル
ヘッダー HTMLホストページの静的HTML
価格差のプラスマイナスの色表示 ダイナミックCSS

詳細: もし求めるウィジェットが無ければ自分で作ることもできるよ。複合ウィジェットや、JavajavaScriptでスクラッチウィジェットを作る詳細については、ディベロッパーガイド Creating Custom Widgets を見てね。

2. UI要素を配置するのに必要なGWTパネルの選択

どのウィジェットを使うかは分かったね。次はGWTパネルを使ってそれらを配置するよ。GWTはレイアウトを管理する様々なパネルがあるよ。パネルは入れ子できるよ。これは HTML で、div 要素や table を使って配置するのに似ているよ。StockWatcher では垂直パネルにネストした水平パネルを使うよ。
http://code.google.com/intl/ja/webtoolkit/doc/1.6/tutorial/images/StockWatcherUIpanel4.jpg

水平パネル

株を追加するの二つの要素(新しい株の記号を入力するボックスと追加ボタン)は機能的に密接に関連していて、一緒に表示したい。それらを横に並べて置くために、TextBoxウィジェットとButtonウィジェットを水平パネルにいれる。Javaのコードでは、HorizontalPanelインスタンスを作成して、addPanel と名前を付けるよ。

垂直パネル

残りの要素を垂直に配置するよ。

これを垂直パネルに入れるよ。Javaコードでは、VerticalPanelインスタンスを作成して、mainPanel と名前をつけるよ。

ルートパネル

ユーザインターフェースでは見えない、もう一つ必要なパネル、ルートパネルがあるよ。ルートパネルは動的要素のための入れ物だよ。これは GWTユーザインターフェース階層の一番上にあるよ。ルートパネルを使うには二通りある。ページのbodyを生成するか、bodyに埋め込まれた特別な要素を生成するかだよ。

ルートパネルは HTMLホストページを包み込んで働くよ。デフォルトでは(ホストページにプレイスホルダーを追加しない場合)、ルートパネルは body 要素を包み込むよ。しかし、ルートパネルを呼ぶときに名前をパラメータで渡すと、任意の要素で包めるよ。次の2行を見ると、どのように動くかをわかるでしょ。

RootPanel.get()             // Default. Wraps the HTML body element.
RootPanel.get("stockList")  // Wraps any HTML element with an id of "stockList"

ホストページは複数のルートパネルを含むことができるよ。例えば、ホストページに複数のGWTウィジェットやパネルを埋め込んでいるとき、それぞれ自分のルートパネルでラップされ独立して実装されるよ。

3. ホストページ StockWatcher.html に アプリケーションの埋め込み

StockWathcerアプリケーションをブラウザで動かすには、HTMLファイルに埋め込む必要があるよ。StockWathcerプロジェクトのホストページ StockWathcer.html は webAppCreator で生成されているよ。スターターアプリケーションのための StockWathcer.html は body 要素が空だよ。その結果、ルートパネルはbody要素を包んでいるよ。ブラウザに表示されているものは全部GWTで構築されているよ。もしアプリケーションが静的要素を持たないなら、HTMLホストページを編集する必要はだろうね。

しかし、StockWathcerは動的要素に加えて、いくつかの静的なHTMLテキスト、画像を使うよ。stockList という名前の <div> 要素のプレイスホルダーを使って、ブラウザにGWTアプリケーションを埋め込むよ。この実装方法は、既存のアプリケーションにGWTを埋め込むのに役に立つよ。

  1. StockWatcher/war/StockWatcher.html ホストページを開く
  2. head要素で、タイトルテキストを StockWatcherに変更する
  3. body要素で、<h1> 見出し StockWathcer を追加
  4. body要素で、<div> 要素を追加して id に stockList を付ける
  5. スターターアプリケーションから、必要ない要素を削除
<!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>

    <!-- 以下削除する
    <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>

注: 簡潔にするため、HTMLコメントは省略している。

4. StockWatcher.javaウィジェットとパネルの実装

次に GWTウィジェットとパネルからユーザインターフェースを構築していくよ。

UI のほとんどは StockWathcer が起動するとすぐに表示されるよ。そのため onMouseLoad メソッドでそれを実装するよ。このセクションでは次のことをするよ。

  1. ウィジェットとパネルのインスタンス
  2. 株データを保持するテーブルの作成
  3. 株追加パネルとメインパネルの配置
  4. メインパネルとルートパネルの関連づけ
  5. 入力ボックスにカーソルフォーカスの移動

このセクションを順に続けていくか、最後の Summary からコピペすることもできるよ。

1. 各ウィジェットとパネルのインスタンス

1. クラスフィールドの初期化子を使って、各ウィジェットとパネルをインスタンス
StockWatcher/src/com/google/gwt/sample/stockwatcher/client/StockWatcher.java を開く。
StockWatcher.java の中で、既にあるスターターアプリケーションのコードを全部、次のコードに置き換える。

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.

  }

}

2. 型を解決できないため、Eclipse が変数定義の警告をする。
Tip: Eclipseの効力を使うには、必要な import宣言を追加するのに suggest機能を使うことだよ。

3. 対応する import宣言の追加
X の忠告が付いているものをクリックする。
エンターを押して、"import EntryPoint (com.google.gwt.core.client.EntryPoint)" を選択する。

4. 同じように他のimport宣言のエラーも解決する。

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. 株データを保持するテーブルの作成

株データを保持するテーブルを実装する。StockWathcerを起動したときに表示するヘッダー行を準備する。記号、価格、差額、削除のそれぞれのカラムのヘッダーのラベルを削除するために setText メソッドを使う。

1. 株データテーブルの作成
onModuleLoad メソッドのなかで、TODOコメントを 以下のコードに置き換える。

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() {
    // 株データテーブルの作成
    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.

  }

}
3. 株追加パネルとメインパネルの配置

ウィジェットをレイアウトするために、株追加パネルとメインパネルの二つを組み立てるよ。最初に入力ボックスと追加ボタンをラップした水平パネル、株追加パネルを組み立てるよ。それから、株リストテーブル、株追加パネル、タイムスタンプをレイアウトした垂直パネル、メインパネルを組み立てるよ。

1. 株追加パネルとメインパネルの中のウィジェットをレイアウトするよ。
onModuleLoadメソッドの中で、TODOコメントを以下のものに置換するよ。

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() {
    // 株データテーブルの作成
    stocksFlexTable.setText(0, 0, "Symbol");
    stocksFlexTable.setText(0, 1, "Price");
    stocksFlexTable.setText(0, 2, "Change");
    stocksFlexTable.setText(0, 3, "Remove");

    // 株追加パネルの組み立て.
    addPanel.add(newSymbolTextBox);
    addPanel.add(addStockButton);

    // メインパネルの組み立て
    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. メインパネルとルートパネルの関連づけ

HTMLホストページに埋め込まれるGWTウィジェットやパネルは、ルートパネルに含まれていなければいけないよ。ルートパネルと垂直パネル mainPanel を関連づけるよ。ルートパネルは、id が "stocklist" の StockWathcer のホストページのHTML要素を包むよ。ここでは それは <div>要素だよ。

1. メインパネルをルートパネルを通したホストページと関連づけるよ
onModuleLoad メソッドの中で、TODOコメントを次のコードと置換するよ。

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() {
    // 株データテーブルの作成
    stocksFlexTable.setText(0, 0, "Symbol");
    stocksFlexTable.setText(0, 1, "Price");
    stocksFlexTable.setText(0, 2, "Change");
    stocksFlexTable.setText(0, 3, "Remove");

    // 株追加パネルの組み立て.
    addPanel.add(newSymbolTextBox);
    addPanel.add(addStockButton);

    // メインパネルの組み立て
    mainPanel.add(stocksFlexTable);
    mainPanel.add(addPanel);
    mainPanel.add(lastUpdatedLabel);

    // メインパネルをHTMLホストページと関連づける
    RootPanel.get("stockList").add(mainPanel);

    // TODO Move cursor focus to the input box.

  }

}

Eclipse はRootPanel に警告して、import宣言を含むように提案するよ。

2. import宣言を含める

import com.google.gwt.user.client.ui.RootPanel;
5. 入力ボックスにカーソルフォーカスの移動

最後に、StockWathcerがロードされたときに、カーソルフォーカスを入力ボックスに移動するよ。

1. onModuleLoad メソッドで、TODOコメントを以下のコードに置き換えるよ。

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() {
    // 株データテーブルの作成
    stocksFlexTable.setText(0, 0, "Symbol");
    stocksFlexTable.setText(0, 1, "Price");
    stocksFlexTable.setText(0, 2, "Change");
    stocksFlexTable.setText(0, 3, "Remove");

    // 株追加パネルの組み立て.
    addPanel.add(newSymbolTextBox);
    addPanel.add(addStockButton);

    // メインパネルの組み立て
    mainPanel.add(stocksFlexTable);
    mainPanel.add(addPanel);
    mainPanel.add(lastUpdatedLabel);

    // メインパネルをHTMLホストページと関連づける
    RootPanel.get("stockList").add(mainPanel);

    // カーソルフォーカスを入力ボックスに移動する
    newSymbolTextBox.setFocus(true);

  }

}
要約

ここまでで出来たことをここで示すよ。

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. ホストモードでレイアウトのテスト

AJAXアプリケーション開発で GWT を使う一つの利点は、ホストモードブラウザを更新してすぐにコードの変更の効果を見ることが出来ることだよ。Eclipse で StockWathcer をデバッグモードで実行して、変更を見ることが出来るよ。StockWathcerを再起動せずに Java パースペクティブ と Debugパースペクティブを切り替えることが出来るよ。

1. 編集したファイルを保存
StockWatcher.java を保存

2. StockWathcerをホストモードで起動
Eclipse のメニューバーから、Run > Debug を選択
もし、Eclipse を使ってないなら、コマンドラインから "ant hosted" を入力

3. ホストモードブラウザは StockWathcerアプリケーションの最初の繰り返しを表示するよ。
http://code.google.com/intl/ja/webtoolkit/doc/1.6/tutorial/images/BuildUI.png

StockWatcher は flexテーブルのヘッダ、入力ボックス、追加ボタンを表示するよ。まだLabelのテキストをセットしてないので何も表示されないよ。株の更新メカニズムを実装すると表示されるでしょう。

4. ホストモードでの実行を離れる
このチュートリアルの残りで、頻繁にホストモードでのテストを行うよ。

ホストモードの更新

ソースコードの変更の後にホストモードでアプリケーションをいつも再起動する必要はないよ。代わりに変更を保存した後にブラウザの再読込ボタンをクリックすれば、自動的に再コンパイルされて新しいバージョンが開くよ。

ベストプラクティス: 時々、再読込していないのに変更されていることに気がつくかもしれないね。これはホストモードがコンパイルされたコードと相互作用している結果だけど、いつもこれが当てになるわけではないよ。これは既存機能の少しの修正の場合だけ起こるよ。変更がしっかり取り込まれるために、いつも再読込する癖を付けた方がいいよ。

つぎは

ここまでで、GWTを使った基本的なUIコンポーネントを構築したよ。まだウィジェットは何も反応しないよ。

これでクライアントのイベントハンドリングの準備が出来たよ。ウィジェットにイベントを聞くように結びつけて、そのイベントに反応するようコードを書くよ。

Step 4: クライアントのイベント管理 - webとかmacとかやってみようか