atwiki-logo
  • 新規作成
    • 新規ページ作成
    • 新規ページ作成(その他)
      • このページをコピーして新規ページ作成
      • このウィキ内の別ページをコピーして新規ページ作成
      • このページの子ページを作成
    • 新規ウィキ作成
  • 編集
    • ページ編集
    • ページ編集(簡易版)
    • ページ名変更
    • メニュー非表示でページ編集
    • ページの閲覧/編集権限変更
    • ページの編集モード変更
    • このページにファイルをアップロード
    • メニューを編集
    • 右メニューを編集
  • バージョン管理
    • 最新版変更点(差分)
    • 編集履歴(バックアップ)
    • アップロードファイル履歴
    • ページ操作履歴
  • ページ一覧
    • ページ一覧
    • このウィキのタグ一覧
    • このウィキのタグ(更新順)
    • このページの全コメント一覧
    • このウィキの全コメント一覧
    • おまかせページ移動
  • RSS
    • このウィキの更新情報RSS
    • このウィキ新着ページRSS
  • ヘルプ
    • ご利用ガイド
    • Wiki初心者向けガイド(基本操作)
    • このウィキの管理者に連絡
    • 運営会社に連絡(不具合、障害など)
とりあえず雑記帳(跡地)
  • ウィキ募集バナー
  • 目安箱バナー
  • 操作ガイド
  • 新規作成
  • 編集する
  • 全ページ一覧
  • 登録/ログイン
ページ一覧
とりあえず雑記帳(跡地)
  • ウィキ募集バナー
  • 目安箱バナー
  • 操作ガイド
  • 新規作成
  • 編集する
  • 全ページ一覧
  • 登録/ログイン
ページ一覧
とりあえず雑記帳(跡地)
  • 新規作成
  • 編集する
  • 登録/ログイン
  • 管理メニュー
管理メニュー
  • 新規作成
    • 新規ページ作成
    • 新規ページ作成(その他)
      • このページをコピーして新規ページ作成
      • このウィキ内の別ページをコピーして新規ページ作成
      • このページの子ページを作成
    • 新規ウィキ作成
  • 編集
    • ページ編集
    • ページ編集(簡易版)
    • ページ名変更
    • メニュー非表示でページ編集
    • ページの閲覧/編集権限変更
    • ページの編集モード変更
    • このページにファイルをアップロード
    • メニューを編集
    • 右メニューを編集
  • バージョン管理
    • 最新版変更点(差分)
    • 編集履歴(バックアップ)
    • アップロードファイル履歴
    • ページ操作履歴
  • ページ一覧
    • このウィキの全ページ一覧
    • このウィキのタグ一覧
    • このウィキのタグ一覧(更新順)
    • このページの全コメント一覧
    • このウィキの全コメント一覧
    • おまかせページ移動
  • RSS
    • このwikiの更新情報RSS
    • このwikiの新着ページRSS
  • ヘルプ
    • ご利用ガイド
    • Wiki初心者向けガイド(基本操作)
    • このウィキの管理者に連絡
    • 運営会社に連絡する(不具合、障害など)
  • atwiki
  • とりあえず雑記帳(跡地)
  • GWT

とりあえず雑記帳(跡地)

GWT

最終更新:2010年07月29日 00:12

fujiyan

- view
管理者のみ編集可
WebコミックLibraryhttp://web-comi.appspot.com/
GAE/JとSlim3で作成してみた、各出版社から配信されているWebコミックをまとめて閲覧できるサイトです。只今、実験運用中…

参考サイト

  • GWTの本家
Google Web Toolkit - Google Code
日本語ページは更新が遅いので、英語ページが良い

  • GWTについては、とりあえずここを見てみよう
Google Web Toolkit (GWT) 入門



構成要素

Module

  • Organizing Projects - Google Web Toolkit - Google Code
  • GWTアプリケーションの構成単位。GWTアプリケーション=1つ以上のModuleの集まり。
    • 1アプリケーションを1つのModuleで表すのも可能だけど、それだとModule定義が肥大化するので、サブシステム単位でModuleを分割するんですよね、きっと
→Moduleは、UIフォームに対応していると考えたほうが良いか?
→Moduleは基本的に[slim3.rootPackage]直下に置け、とのこと。そうなると、あまり数を増やすわけにもいけないので、やっぱサブシステム単位で?
  • Moduleは[モジュール名].gwt.xmlファイルで定義される。定義を構成する要素として下記のものがある。
    • 継承元のModule
      • クラス継承みたいな仕組みというよりは、importに近いイメージ。必要に応じて必要なModuleをinheritsで指定する(複数可)。
      • 通常用いる、標準Moduleの一覧:Organizing Projects - Google Web Toolkit - Google Code
    • Entry Pointクラス
      • 後述
      • HTMLから参照されるModuleの場合、必ず1つ以上のEntry Pointクラスの記述が必要にある。逆に、HTMLから参照されないModule(継承されることを前提としたModule等)ならば必須ではない。
    • sourceパス
      • JavaScriptに変換されるJavaソースファイルを置くところ
    • publicパス
      • 静的リソースファイル(CSS/画像ファイル等)を置くところ。例えばgwt.xmlファイルの<stylesheet>要素のsrc属性で相対パスを指定した場合、publicパスが起点となる。

theme

  • ModuleのXMLファイルにて、UIのthemeを設定できる。設定しておかないと、DialogBoxとか透明になってしまう。
  • 設定するには、themeのModuleを継承する。下記はChromeの場合
	<inherits name='com.google.gwt.user.theme.chrome.Chrome'/>
  • 他にStandardとDarkがある。

themeのスタイルの一部を変更する。

  • themeのスタイルで気に入らないところがあれば上書きが可能
  • gwt.xmlファイルの<stylesheet>属性で、変更後のスタイルを記述したcssファイルを指定する。
    • 相対パスで指定した場合は、<public>要素のpath属性で指定した場所が起点となる。
    <stylesheet src="foo.css"/>
  • cssファイルに変更後のスタイルを記述する。下記の場合、全般的にフォントサイズを10pt、Buttonだけフォントサイズを20ptにする。
* { font-size: 10pt; }
.gwt-Button { font-size: 20pt; }



Entry Pointクラス

  • クライアント(ブラウザ)からのアクセスを受け付ける。
  • onModuleLoad()メソッドを唯一持ち、Entry Pointクラスを参照するHTMLの表示時にメソッドが呼び出される。メソッド内でコントロールの初期化やイベントハンドラの設定等を行う。

HTML

  • <script>タグで、参照するModuleを呼び出す。
<script type="text/javascript" language="javascript" 
src="jp.fujiyan.gae.example.slim3.gwt.Main/jp.fujiyan.gae.example.slim3.gwt.Main.nocache.js"></script>
  • Moduleが呼び出されると、Moduleで定義されたEntry PointクラスのonModuleLoad()メソッドが呼び出される。
  • HTMLで、GWTウィジェットを配置したい場所に<div>タグを定義し、Entry PointクラスのonModuleLoad()内で、定義したレイアウトを<div>タグに設定する。

HTML
<div id="gwtDiv"></div>
Entry Point
private VerticalPanel mainPanel = new VerticalPanel();
public void onModuleLoad() {

  (mainPanelにレイアウト設定)

  RootPanel.get("gwtDiv").add(mainPanel);
}


UiBinder

  • Entry Pointクラスだけでレイアウトを作成する場合、onModuleLoad()メソッドの中のJavaコードでレイアウトを実装する必要がある。
  • そこで、GWT2.0から導入されたUiBinderを用いると、XML(or HTML)でレイアウトの定義が可能になる。
  • レイアウト記述するXMLファイルと、イベントハンドラなどを実装するクラスの2つから成り立つ。
<!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">
	<ui:style>
		/* Add CSS here. See the GWT docs on UI Binder for more details */
		.important {
			font-weight: bold;
		}
	</ui:style>
	<g:HTMLPanel>
		<!--
			Add GWT widgets here. Enter < and hit Ctrl-Space to auto-complete
			widget names. The ui:field attribute binds an element to a field in
			the owner class. See the GWT docs on UI Binder for more details.
		-->
		Hello,
		<g:Button styleName="{style.important}" ui:field="button" />
	</g:HTMLPanel>
</ui:UiBinder>
public class FooView extends Composite {

    private static FooViewUiBinder uiBinder =
        GWT.create(FooViewUiBinder.class);

    interface FooViewUiBinder extends UiBinder<Widget, FooView> {
    }

    @UiField
    Button button;

    public FooView(String firstName) {
        initWidget(uiBinder.createAndBindUi(this));

        // Can access @UiField after calling createAndBindUi
        button.setText(firstName);
    }

    @UiHandler("button")
    void onClick(ClickEvent e) {
        Window.alert("Hello!");
    }
}
  • <g:Button>がwidgetのタグ。ui:fieldで指定した名前と、Javaクラスで@UiFieldアノテーションを適用したフィールド名を同じにすることで関連づける(上記の場合、"button")。
  • イベントハンドラは、メソッドに@UiHanderアノテーションを適用する。
    • アノテーションで、イベントソースとなるwidgetのui:field名を指定する(上記の場合、"button")
    • ハンドラの引数のクラスで、ハンドリングするイベントを指定できる(上記の場合、ClickEventが引数なので、Clickイベントがハンドリングできる)
    • よって、イベントハンドラのメソッド名は好きにしてよい。



Module作成

  1. メニューの[File]-[New]-[Module](またはプロジェクトのコンテキストメニュー→[New]-[Other...]-[Google Web Toolkit]-[Module])を選択し、[Package]にModuleのパッケージ名(先のslim3.rootPackageで指定したパッケージ以下にすること)、[Module name]に作成するModule名(例=Main)を記述し、[Finish]をクリックする。
  1. 上記で作成された、src/[Moduleのパッケージ名/[Module名].gwt.xml"を下記のように修正する。
<module>
  <inherits name="com.google.gwt.user.User" />
  <inherits name='org.slim3.gwt.emul.S3Emulation' />
  <inherits name='com.google.gwt.user.theme.standard.Standard' />

  <source path="client" />
  <source path="shared" />
</module>



Entry Pointクラス作成

  1. メニューの[File]-[New]-[Entry Point Class](またはプロジェクトのコンテキストメニュー→[New]-[Other...]-[Google Web Toolkit]-[Entry Point Class])を選択し、[Package]に[Moduleのパッケージ名].client、[Name]に作成するEntry Pointクラス名([Module名]EntryPoint)を記述し、[Finish]をクリックする。



HTMLページ作成

  1. メニューの[File]-[New]-[HTML Page](またはプロジェクトのコンテキストメニュー→[New]-[Other...]-[Google Web Toolkit]-[HTML Page])を選択し、[File name]に作成するHTMLページのファイル名を記述し、[Finish]をクリックする。



UiBinder作成

  1. メニューの[File]-[New]-[UiBinder](またはプロジェクトのコンテキストメニュー→[New]-[Other...]-[Google Web Toolkit]-[UiBinder])を選択し、[Package]に[Moduleのパッケージ名].client、[Name]に作成するUiBinder名([Module名]と同じ?)を記述し、[Finish]をクリックする。
  1. Entry Pointクラスを下記のように修正する。
    public void onModuleLoad() {
        RootPanel.get().add(new Main("fujiyan"));
    }



実行

  1. プロジェクトのコンテキストメニュー→[Google]→[GWT Compile]で[GWT Compile]ダイアログを開き、[Compile]をクリック(または、ツールバーのアイコンをクリック)
  2. Web Application起動
  3. ブラウザからアクセス
  • チュートリアル通りだと、下記のようなアプリになる。

GWT module [Module名] may need to be (re)compiled

  • ブラウザからのアクセス時に、上記のようなメッセージが表示された場合は、プロジェクトのコンテキストメニュー→[Google]→[GWT Compile]で[GWT Compile]ダイアログを開き、[Compile]をクリック(または、ツールバーのアイコンをクリック)



戻るボタン管理(かなり試行錯誤中…)

  • GWTの画面遷移はページの切替ではなく、JavaScriptによる画面の書き換えのため、戻るボタンを押しても前の画面に戻ることは無い。
  • HistoryクラスとHyperlinkクラスを利用することで、画面遷移時にイベントを発生させ、ハンドリングすることができる。これは戻るボタンが押された際にもイベントを発生させることができる。
  • イベントが発生するだけなので、実際にどの画面を表示するべきかは自前で実装する必要がある。Hyperlinkクラスを使えば、勝手に戻るボタンの動きが実現されるわけでは無いので注意。
  • 各画面に対してtokenを定義する。tokenは画面を一意に識別するためのものであり、画面遷移イベント発生時に、次に表示すべき画面に割り当てられたtokenがパラメータとして渡される。それを元に、対応する画面の表示処理を記述する。

画面遷移のイベント取得

  • EntryPointクラスのonModuleLoad()メソッド内で、Historyクラスに対してValueChangeHandlerを設定する。
public void onModuleLoad() {
    // 初期画面のtokenを設定する。
    String initToken = History.getToken();
    if (initToken.length() == 0) {
        History.newItem("first");
    }
    
    RootPanel.get().add(new FirstView());//初期画面表示(本題には関係無し)
    
    // 戻るボタンクリック時のイベントハンドラ設定
    History.addValueChangeHandler(new ValueChangeHandler<String>(){
        // このメソッドが呼ばれます。
        public void onValueChange(ValueChangeEvent<String> event) {
            //例として、戻るボタンクリック後に遷移する画面のtokenをalertで表示します。
            Window.alert(event.getValue());
        }
    });
}

  • 次画面に遷移するHyperlinkのtargetHistoryToken属性に、次画面のtokenを記述しておく。
<g:Hyperlink ui:field="nextViewLink" targetHistoryToken="next">次画面</g:Hyperlink>

  • 上記のコードによって、次画面遷移時には"next"がalertで表示され、戻るボタンをクリックすると"first"がalertで表示される。

  • ということで、画面遷移の処理はリンクのクリックイベントハンドラで記述するのではなく、History.addValueChangeHandler()に与えたValueChangeHandlerで記述するのがイイみたい?

  • 以下、現時点での試行錯誤の結果。参考:Coding Basics - History

  • EntryPointのonModuleLoad()では、History.fireCurrentHistoryState();でViewChangeEventを発生させ、初期画面を表示させる。
public class FooEntryPoint implements EntryPoint {
    public void onModuleLoad() {
        History.addValueChangeHandler(new FooViewManager());
        History.fireCurrentHistoryState();
    }
}
  • FooViewManagerで、tokenを解析して表示させる画面を決定し、パラメータを渡す。
public class FooViewManager implements ValueChangeHandler<String> {
    /**
     * tokenをパラメータのMapに変換する。
     * 
     * @param token
     * @return パラメータのMap
     */
    public static Map<String, String> toParameterMap(String token) {
        Map<String, String> map = new HashMap<String, String>();
        String[] params = token.split("&");
        for (String param : params) {
            int pos = param.indexOf('=');
            String name = URL.decodeComponent(param.substring(0, pos));
            String value = URL.decodeComponent(param.substring(pos + 1));
            map.put(name, value);
        }
        
        return map;
    }
    
    /**
     * パラメータのMapをtokenに変換する。
     * 
     * @param parameterMap パラメータのMap
     * @return token
     */
    public static String toToken(Map<String, String> parameterMap) {
        StringBuilder sb = new StringBuilder();
        for (String name : parameterMap.keySet()) {
            if (sb.length() > 0) {
                sb.append('&');
            }
            String value = parameterMap.get(name);
            sb.append(URL.encodeComponent(name)).append('=').append(URL.encodeComponent(value));
        }
        
        return new String(sb);
    }
    
    public void onValueChange(ValueChangeEvent<String> event) {
        RootPanel root = RootPanel.get();
        root.clear();
        
        Composite nextView;
        String token = event.getValue();
        // tokenをパラメータのMapに変換
        Map<String, String> parameterMap = toParameterMap(token);
        // viewパラメータで表示する画面を決定させる
        String view = parameterMap.get("view");
        if ((view == null) || view.equals("firstView")) {
            // viewパラメータがnullの場合は初期表示
            nextView = new FirstView();
        } else if (view.equals("secondView")) {
            // view以外のパラメータの使い方は各画面に依存するので、ここではMapをそのまま渡すだけ
            nextView = new SecondView(parameterMap);
        } else {
            Window.alert("undefined view=[" + view + "]");
            return;
        }
        root.add(nextView);
    }
}
  • tokenとパラメータのMapとの変換で用いているURL.encodeComponent()/decodeComponent()は、URL-encodeを行うためのメソッド。
    • URL.encode()/decode()では&や=を変換してくれないので、URL.encodeComponent()/decodeComponent()を利用
    • URLクラスを用いるためには、gwt.xmlファイルで、com.google.gwt.http.HTTP moduleをinheritしておく必要がある。
<module>
    <!-- 略 -->
    <inherits name="com.google.gwt.http.HTTP"/>
    <!-- 略 -->
</module>

  • パラメータを受け取る画面のコンストラクタで、実際に必要なパラメータを取得して画面表示に反映させる。
public class SecondView extends Composite {
    public SecondView (Map<String, String> parameterMap) {
        initWidget(uiBinder.createAndBindUi(this));

        String name = parameterMap.get("name");
        String kana = parameterMap.get("kana");
        if (name != null) {
            // パラメータがあれば、その内容で画面表示
            nameTextBox.setText(name);
            kanaTextBox.setText(kana);
        }
    }
}

  • パラメータが不要な画面へのリンクはviewパラメータのみにする。もちろん、リンクのClickイベントで次画面を生成する処理とかは不要。
<g:Hyperlink targetHistoryToken="view=firstView">初期画面</g:Hyperlink>

  • 検索画面で、検索結果を履歴に含めたい場合は、検索処理の後に検索パラメータをtokenに持たせたURLを履歴に加えれば良い。
HashMap<String, String> parameterMap = new HashMap<String, String>();
parameterMap.put("view", "secondView");
parameterMap.put("name", name);
parameterMap.put("kana", kana);
History.newItem(FooViewManager.toToken(parameterMap), false);



UiBinderでDialogBox(正解不明、ごまかし版)

  • DialogBoxもUiBinderで作れたら楽だよね
  • でも、呼び出し元のXMLに<g:DialogBox>を埋め込むのはイヤ。他画面からの再利用ができない。

手順

  • とりあえず、通常の手順(メニューの[File]-[New]-[UiBinder])でUiBinderを作成する。
  • ui.xmlファイルを修正する。
    • <ui:Binder>の直下のWidgetは、Panel系のWidget1つ(下記の場合は<g:VerticalPanel>)のみ。
<!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">
    <ui:style>
    </ui:style>
    <g:VerticalPanel>
        <g:TextBox ui:field="valueTextBox" visibleLength="40"/>
        <g:Button ui:field="closeButton">閉じる</g:Button>
    </g:VerticalPanel>
</ui:UiBinder>

  • .javaファイルを修正する。
    • 親クラスをCompositeからDialogBoxに変更する。
    • コンストラクタ内のinitWidget(uiBinder.createAndBindUi(this))を、add(uiBinder.createAndBindUi(this))に変更する。
    • setText()でダイアログのキャプション(タイトルバーのテキスト)を設定する。
    • 閉じるのはhide()メソッド。
public class TestDialog extends DialogBox {

    private static TestDialogUiBinder uiBinder =
        GWT.create(TestDialogUiBinder.class);

    interface TestDialogUiBinder extends UiBinder<Widget, TestDialog> {
    }
    
    public TestDialog() {
        add(uiBinder.createAndBindUi(this));
        setText("テストダイアログ");
    }

    @UiField
    TextBox valueTextBox;
    
    @UiField
    Button closeButton;

    @UiHandler("closeButton")
    public void onClick_closeButton(ClickEvent e) {
        hide();
    }
}

  • 呼び出し
    • インスタンス生成後、位置を設定しないと表示されない。下記の例では、center()で中央に設定する。
    @UiHandler("dialogButton")
    public void onClick(ClickEvent e) {
        TestDialog dialogBox = new TestDialog();
        dialogBox.center();
        dialogBox.show();
    }

欠点

  • タイトルのキャプションはXMLで設定できない。



PushButtonとVerticalPanel

  • PushButtonをVerticalPanelの直下に配置すると、幅がVerticalPanelのサイズに合わせられる。
  • つまり、VerticalPanel直下で最も大きいwidgetの幅に合わせられる。
  • 回避するためには、PushButtonをHorizontalPanelで囲んでおく。
<g:VerticalPanel>
    <g:HorizontalPanel>
        <g:PushButton ui:field="testbutton">ボタン</g:PushButton>
    </g:HorizontalPanel>
    <g:TextBox ui:field="nameTextBox" visibleLength="40"/>
    <g:TextBox ui:field="kanaTextBox" visibleLength="80"/>
</g:VerticalPanel>



Moduleのjarファイル化(Module jarファイル)

参考Mark Volkmann
  • 再利用可能なModuleをjarファイル化して、GWTにおけるライブラリとして用いる。
  • 簡単に言えば、通常のjarファイル内に、javaソースファイルとgwt.xmlファイルを含ませればいい。

作成方法

  • 通常のjavaプロジェクトを作成する。
  • プロジェクトのクラスパスにApp Engine SDKとGWT SDKを追加する
    • プロジェクトのプロパティダイアログを開き(プロジェクトのコンテキストメニュー→[Properties])、[Java Build Path]→[Add Library...]で、下記2つを追加
      • Google App Engine
      • Google Web Toolkit
  • srcフォルダ内にルートパッケージ(任意のパッケージでOK。例:jp.fujiyan.gwtroot)を作成する。
    • クライアント側で利用するクラスはルートパッケージ以下に作成する(サーバー側のみで利用でするクラスであれば、ルートパッケージ以下で無くても良い)。
  • ルートパッケージ直下にgwt.xmlファイルを作成する。
    • ファイル名が、利用する側で指定するModule名になる(Foo.gwt.xmlならFooをModule名として指定)。
    • gwt.xmlファイル内で、クライアント側で利用するクラスを格納するフォルダ(ルートパッケージからの相対パス)を<source>要素で指定する。
    • jp.fujiyan.gwtroot.clientにクライアント側で利用するクラスを格納する場合
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.0.3//EN" "http://google-web-toolkit.googlecode.com/svn/tags/2.0.3/distro-source/core/src/gwt-module.dtd">
<module>
    <source path="client" />
</module>
Module jarのプロジェクトではGWTコンパイルを実施しないので、<inherits>でUser等のModuleを参照する必要は無い(よね? 多分…)

  • クラスを作成する。
    • 上述の通り、クライアント側で利用するクラスは、<source>要素で指定したフォルダ以下に作成する。
    • サーバー側のみの場合は自由に…
  • jarファイルにパッケージングする。
    • classファイルと、そのソースのjavaファイルは、同じフォルダに格納しておく。
    • もちろん、gwt.xmlファイルもルートパッケージのフォルダに格納しておく。
    • ソースが必要なのはクライアント側で利用するクラスの分だけなので、サーバー側のみで利用するクラスについては、javaファイルを同梱しなくてもよい。

利用方法

  • Module jarファイルをプロジェクトのクラスパスに追加する。
  • 利用するModuleのgwt.xmlファイルで、Module jarファイルのModuleを継承するように指定する。
    • Module jarファイル内で、gwt.xmlファイルがjp/fujiyan/gwtroot/Foo.gwt.xmlにある場合、下記の様に指定する。
    <inherits name="jp.fujiyan.gwtroot.Foo"/>



JavaScript Native Interface(JSNI)

参考
Coding Basics - JavaScript Native Interface (JSNI)
Java開発者のためのAjax: Google Web Toolkitを探る - developerWorks Japan

  • Javaソース内でJavaScriptのコードを記述する。
    • nativeメソッドとして宣言
    • JavaScriptを記述するメソッド本体は/*- -*/で囲む
public static native void alert(String msg) /*-{
    $wnd.alert(msg);
}-*/;

JavaScriptObject

参考
Google Web Toolkit Blog
  • Java側とJavaScript側とでオブジェクトを交換する際の、JavaScriptのObjectに対するJava側のクラス
  • JavaScriptをextendsしたクラスを作成し、JavaScriptのObjectのプロパティにアクセスするためのgetter/setterをJSNIで記述する。
  • Java側では、一旦JavaScriptObjectで受け取り、extendsしたクラスにキャストする。
class FooObject extends JavaScriptObject {
    protected FooObject() {} 

    public final native String getText() /*-{ return this.text; }-*/;
}
package jp.fujiyan.jsni.client;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.Window;

public class JsniTest {
	private native void foo() /*-{
		var obj = new Object();
		obj.text = "test";
		this.@jp.fujiyan.jsni.client.JsniTest::bar(Lcom/google/gwt/core/client/JavaScriptObject;)(obj);
	}-*/;
	
	public void bar(JavaScriptObject data) {
		Window.alert(((FooObject) data).getText());
	}
}



JavaScriptから呼び出されるコールバックメソッド内から他のメンバにアクセスできない

参考
Java開発者のためのAjax: Google Web Toolkitを探る - developerWorks Japan
Test.java
public class Test implements EntryPoint, FooHandler {
	private Foo foo = new Foo();
	
	public native void barJava(FooHandler h) /*-{
		barJs(h.@jp.fujiyan.test.client.foo.FooHandler::callback());
//		下記コードならcallback()内からfooにアクセスできる
//		h.@jp.fujiyan.test.client.foo.FooHandler::callback()();
	}-*/;

	public void callback() {
		Window.alert(String.valueOf(foo));
	}
	
	
	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad() {
		barJava(this);
	}
}
FooHandler.java
package jp.fujiyan.test.client.foo;

public interface FooHandler {
	void callback();
}
bar.js
function barJs(callback) {
	callback();
}
Test.html
~略
  <head>
~略
    <script type="text/javascript" language="javascript" src="bar.js"></script>
    <script type="text/javascript" language="javascript" src="test/test.nocache.js"></script>
~略
  </head>
~略

  • 上記のコードの場合、まずTest#onModuleLoad()が呼び出され、TestクラスのインスタンスがbarJava()に渡される。
  • barJava()はJSNIで記述されており、渡されたFooHandlerのcallback()インスタンス(Functionのインスタンス)がbarJs()(外部ファイルbar.jsで定義されているので、Testクラスのメンバ?ではないfunction)に渡される。
  • barJs()では、渡されたFunctionのインスタンス(=FooHandler#callback())を呼び出す。
  • 呼び出されたcallback()内で、外部クラスのインスタンスフィールドであるfooにアクセスしようとするが、fooが未定義とみなされていまう。
  • これは、fooのアクセスについて、GWTコンパイラがthis.fooと変換するが(実際はfooというシンボルはGWTコンパイラが適当に変換するけど…)、このときのthisが外部クラスを指しておらず、fooが定義されていないとしてエラーとなる。
  • たぶん、このときのthisは、Functionのインスタンスを指しているから?
  • ということで、Java側のメソッドをコールバック関数に直接指定してはダメ。
  • 対処方法として、JavaScript側でラッパー関数を作成し、それをコールバック関数に指定する。ラッパー関数はインスタンスとメソッドを受け取り、コールバックされた際にcall()/apply()メソッドを用いて、受け取ったインスタンスをthisとしてメソッドを呼び出す。
Test.java
public class Test implements EntryPoint, FooHandler {
	private Foo foo = new Foo();
	
	public native void barJava(FooHandler h) /*-{
		// ラッパー関数を呼び出す。
		wrappedBarJs(h, h.@jp.fujiyan.test.client.foo.FooHandler::callback());
	}-*/;

	public void callback() {
		Window.alert(String.valueOf(foo));
	}
	
	
	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad() {
		barJava(this);
	}
}
bar.js
function barJs(callback) {
	callback();
}

// ラッパー関数
// instanceは、コールバック関数内でthisにしたいインスタンス
function wrappedBarJs(instance, callback) {
	barJs(
		function() {
			callback.apply(instance);
		}
	);
}



Channel API(作成中)

※API自体正式版でないので変更の可能性あり
参考
スティルハウスの書庫
Dance Dance Robot!(Channel APIのデモ)

  • Channel APIを使えば、サーバーからのpush通信でクライアントにイベントを発生させることが可能
  • GAE SDK1.3.5時点でJavadocには記載されていないものの、jarファイル内にはクラスが存在している模様
  • クライアント側のJavaScriptはhttp://talkgadget.google.com/talkgadget/channel.jsを利用。GWTからJSNIでアクセスする。
直接アクセスせずに、appengine-api.jar内から/apphosting/tools/dev-channel-js.jsを抽出し、それをwarディレクトリ内に配置せよ、とのこと。
配置の際には、warディレクトリに/_ah/channelディレクトリを作成し、その中に抽出したjsファイルを"jsapi"にリネームして配置する。
参考:Dance Dance Robot error / Channel API
    • 上記パスで指定しておけば、1.3.6から正式リリースされた際にもパスの修正不要らしい。
  • そのうちGWTのサポートクラスが出来るらしい。それまではとりあえずDance Dance Robot!にある
    • com.google.appengine.demos.dda.client.channel.Channel
    • com.google.appengine.demos.dda.client.channel.ChannelFactory
    • com.google.appengine.demos.dda.client.channel.Socket
    • com.google.appengine.demos.dda.client.channel.SocketListener
をまるパクリする(汗
  • Serverから送ることができるのはString
    • よって、オブジェクトをStringにエンコードして送ることになる。
    • Dance Dance Robotでは、com.google.gwt.user.server.rpc.RPCを使ってオブジェクトをエンコードして送っている。
      • 本来はRCP.encodeResponse()でエンコードすれば楽だけど、privateメソッドらしいので仕方なくRCP.encodeResponseForSuccess()でエンコードしている。そのため、いちいちダミーメソッドを定義したりと回り道。
「GWT」をウィキ内検索
LINE
シェア
Tweet
添付ファイル
  • GWT001.png
  • GWT002.png
  • GWT003.png
  • GWT004.png
  • GWT005.png
  • GWT006.png
とりあえず雑記帳(跡地)
記事メニュー

メニュー

  • トップページ
  • コメント
  • とりあえずインターフェース入門
  • Yesod
  • Haskell
  • Slim3
  • JavaScript
  • Google App Engine
  • Android
  • Facebook
  • GWT
  • OpenSocial
  • Struts 2
  • Subversion
  • Apache
  • JSONIC
  • Flex

  • WebコミックLibraryについて

公式サイト

  • Yesod
  • Haskell
  • Slim3
  • JavaScript - MDN
  • App Engine for Java
  • Android Developers
  • Google Web Toolkit
  • Struts
  • OpenSocial
  • OSDE

  • ToDo

ここを編集
記事メニュー2
間違いの御指摘は
コメントまでm(_ _)m

更新履歴

取得中です。


ここを編集

総数: -
本日: -
昨日: -
人気記事ランキング
  1. Slim3
もっと見る
最近更新されたページ
  • 4363日前

    Haskell
  • 4363日前

    Yesod
  • 4363日前

    トップページ
  • 4473日前

    メニュー
  • 4474日前

    Struts 2
  • 4500日前

    コメント
  • 4669日前

    Google App Engine
  • 4706日前

    Slim3/環境構築とプロジェクト作成
  • 4731日前

    Google App Engine/キャッシュの計画
  • 4741日前

    Slim3/文字列の部分一致検索とページング
もっと見る
人気記事ランキング
  1. Slim3
もっと見る
最近更新されたページ
  • 4363日前

    Haskell
  • 4363日前

    Yesod
  • 4363日前

    トップページ
  • 4473日前

    メニュー
  • 4474日前

    Struts 2
  • 4500日前

    コメント
  • 4669日前

    Google App Engine
  • 4706日前

    Slim3/環境構築とプロジェクト作成
  • 4731日前

    Google App Engine/キャッシュの計画
  • 4741日前

    Slim3/文字列の部分一致検索とページング
もっと見る
ウィキ募集バナー
新規Wikiランキング

最近作成されたWikiのアクセスランキングです。見るだけでなく加筆してみよう!

  1. MadTown GTA (Beta) まとめウィキ
  2. GTA5 MADTOWN(β)まとめウィキ
  3. R.E.P.O. 日本語解説Wiki
  4. シュガードール情報まとめウィキ
  5. SYNDUALITY Echo of Ada 攻略 ウィキ
  6. ドタバタ王子くん攻略サイト
  7. ガンダムGQuuuuuuX 乃木坂46部@wiki
  8. パズル&コンクエスト(Puzzles&Conquest)攻略Wiki
  9. ありふれた職業で世界最強 リベリオンソウル @ ウィキ
  10. Idol Showdown(アイドルショーダウン)日本 @ ウィキ
もっと見る
人気Wikiランキング

atwikiでよく見られているWikiのランキングです。新しい情報を発見してみよう!

  1. アニヲタWiki(仮)
  2. ストグラ まとめ @ウィキ
  3. ゲームカタログ@Wiki ~名作からクソゲーまで~
  4. 初音ミク Wiki
  5. MadTown GTA (Beta) まとめウィキ
  6. 発車メロディーwiki
  7. 機動戦士ガンダム バトルオペレーション2攻略Wiki 3rd Season
  8. 検索してはいけない言葉 @ ウィキ
  9. オレカバトル アプリ版 @ ウィキ
  10. Grand Theft Auto V(グランドセフトオート5)GTA5 & GTAオンライン 情報・攻略wiki
もっと見る
全体ページランキング

最近アクセスの多かったページランキングです。話題のページを見に行こう!

  1. 魔獣トゲイラ - バトルロイヤルR+α ファンフィクション(二次創作など)総合wiki
  2. 参加者一覧 - ストグラ まとめ @ウィキ
  3. 品川駅 - 発車メロディーwiki
  4. Famos Gray - ストグラ まとめ @ウィキ
  5. 参加者一覧 - MadTown GTA (Beta) まとめウィキ
  6. 発車メロディー変更履歴 - 発車メロディーwiki
  7. Lycoris - MadTown GTA (Beta) まとめウィキ
  8. オーバーモード/マスターモード(仮面ライダーガヴ) - アニヲタWiki(仮)
  9. FadeGate - ストグラ まとめ @ウィキ
  10. ぶんぶんギャング - MadTown GTA (Beta) まとめウィキ
もっと見る

  • このWikiのTOPへ
  • 全ページ一覧
  • アットウィキTOP
  • 利用規約
  • プライバシーポリシー

2019 AtWiki, Inc.