GWT Widgetを拡張する

GWTWidgetは階層に基づいたクラス設計がなされている為、簡単に拡張できる。

例) input要素に追加されたplaceholder属性をプロパティとして利用することのできるTextBoxを作る

元々GWTのTextBoxクラス(com.google.gwt.user.client.ui.TextBox)はinput要素のピアとして提供されている。 このクラスにplaceholderプロパテイを追加するように実装してみよう。

既存のTextBoxを拡張する

placeholderプロパティを追加したTextBox5というクラスを実装してみよう。

public class TextBox5 extends TextBox {
    public String getPlaceHolder() {
        return DOM.getElementProperty(this.getElement(), "placeholder");
    }
    public void setPlaceHolder(String placeholder) {
        DOM.setElementProperty(this.getElement(), "placeholder", placeholder);
    }
}

簡単だ。実装のポイントはDOM.get/setElementPropertyメソッド。このメソッドでピアの要素(input要素)の属性を取得/設定することができるので、HTMLの仕様が変わっても、プラットホームによって違っても対応が可能だ。
この例の場合、placeholder属性の読み書きを行っている。

モジュール化する

順序が逆になったがGWTで使用するクラスはなんらかのモジュールに属している必要がある。本来、ある程度まとまった実装(Widget全部とか)をモジュール化するのだろうが、今回は一つのクラスだけが属しているモジュールとして、Kazz.gwt.xmlを作成する。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.2.0//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.2.0/distro-source/core/src/gwt-module.dtd">
<module rename-to="kazz">
  <inherits name="com.google.gwt.user.User" />
  <inherits name="com.google.gwt.i18n.I18N"/>
  <inherits name="com.google.gwt.resources.Resources" />
  <source path="client" />
</module>

基本的には必要な他のモジュールをinheritするだけで良いと思う。

他のモジュールで参照する

他のモジュールで参照する場合、Javaクラスのクラスパスとは別にモジュールをinheritする必要がある。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.2.0//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.2.0/distro-source/core/src/gwt-module.dtd">
<module rename-to="addressbook">
  <inherits name="org.kazz.gwt.Kazz" /> <!-- 使用するモジュールをInherit -->
  <source path="client" />
  <entry-point class="net.kazzz.client.AddressBook" />
</module>
UiBinderからTextBox5を利用する

上記方法で実装、モジュール化したWidgetGWTアプリケーションからは勿論のこと、UiBinderから使うこともできる。

これはGWTDesignerで拙作の住所録UIをUiBinderで作成している所だが、入力に使用しているTextBoxはTextBox5クラスを使用しているのが分かる。実際にplaceholderプロパティを設定しているのも分かるだろう。まだコンポーネントパレットに登録していないのでXML側ではネームスペースも含めて手動で追加する必要がある。

  • AddressBookUi.ui.xml
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder
 xmlns:ui="urn:ui:com.google.gwt.uibinder"
 xmlns:g="urn:import:com.google.gwt.user.client.ui"
 xmlns:j="urn:import:org.kazz.gwt.client.widget"   <== TextBox5を使用するために追加したネームスペース
>
 <g:HTMLPanel styleName="{style.AddressInfo}">
   <g:Grid ui:field="pnlAddressInfo">
     <g:row>
       <g:customCell><g:Label ui:field="lblFullName" text="氏名"/></g:customCell>
       <g:customCell><j:TextBox5 ui:field="txtFullName" visibleLength="20" placeHolder="例) 山田太郎" /></g:customCell>
     </g:row>
     <g:row>
       <g:customCell><g:Label ui:field="lblAddress" text="住所"/></g:customCell>
       <g:customCell><j:TextBox5 ui:field="txtAddress" visibleLength="50" placeHolder="例) 東京都千代田区外神田"/></g:customCell>
     </g:row>
     <g:row>
       <g:customCell><g:Label ui:field="lblEmail" text="e-mail"/></g:customCell>
       <g:customCell><j:TextBox5 ui:field="txtEmail" visibleLength="25" placeHolder="例) mail@example.com"/></g:customCell>
     </g:row>
     :
     :
</ui:UiBinder> 

ここで素晴らしいのは拡張したクラスに合わせて追加したクラス(TextBox5)のためのXML名前空間もきちんと補完されることだ。

前回のエントリで解説したように、UiBinderで定期したクラスはui:fieldで指定したフィールド名で参照することができる。

  • AddressBookUi.java
    @UiField Label lblFullName;
    @UiField TextBox5 txtFullName;

    @UiField Label lblAddress;
    @UiField TextBox5 txtAddress;

    @UiField Label lblEmail;
    @UiField TextBox5 txtEmail;
実行結果 (PC Chrome12)

実行結果 (Android 2.3.3 Webkit Emulator)

実行結果を見ると、空のinput要素にプレースホルダ(placeholder)属性が設定されているのが分かるだろう。※


以上、GWTのルールに従うという面倒さはあるものの、GWT Widgetの拡張自体は非常に簡単にできる。これで業務に合わせたWidgetクラスをどんどん作れそうだ。

※placeholder属性は残念ながらIEでは動作しない(無視される)