Titanium の WebView で HTML をホストする方法の覚え書き。
目次
WebView
WebView は Titanium.UI.WebView として定義されるブラウザ コントロールである。URL か HTML ソースを指定することでページが表示される。例えば以下のように使用する。
var window = Titanium.UI.createWindow();
// URL
var webView = Titanium.UI.createWebView( {
url: "http://www.example.com/"
});
// HTML を直に指定
var webView2 = Titanium.UI.createWebView( {
html: "<!DOCTYPE html><html>... 中略 ...</html>"
});
window.add( webView );
window.add( webView2 );
window.open();
アプリと WebView 内ページの連携
webView は、単にページを表示するだけでなく、アプリと連携をおこなう仕組みも実装されている。
アプリ側からの通知を受け取る場合、WebView 内ページの JavaScript で以下のようなハンドラ関数を定義する。
/**
* Titanium アプリからの通知によって発生します。
*
* @param {Object} params Titanium アプリから指定されたパラメータ。
*/
function onTitanium( params ) {
// params を利用した処理 ...
}
アプリ側の WebView インスタンスが webView の場合、この関数は以下のように呼び出す。
var js = 'onTitanium( { text: "テキスト" } )';
webView.evalJS( js );
WebView.evalJS メソッドは eval という名前のとおり、指定された JavaScript を評価する。よってページ内に読み込まれた関数の呼び出しを記述すれば、それが実行される。
次は WebView 内のページ側からアプリへの通知。まず、アプリ側にイベント リスナーを追加する。
/**
* WebView 内ページからの通知によって発生します。
*
* @param {Object} params Titanium アプリから指定されたパラメータ。
*/
Titanium.App.addEventListener( "onWebView", function( params ) {
// params を利用した処理 ...
} );
ページ側は、これを以下のように呼び出す。
// Ti.App.fireEvent でも可
Titanium.App.fireEvent( "onWebView", { text: "テキスト" } );
WebView 内に読み込まれたページには Titanium.App.fireEvent という特殊なメソッドが定義されており、これがアプリ側への橋渡しを担う。アプリ→ページのときと異なり、パラメータには JavaScript のオブジェクトをそのまま指定できる。
これらの仕組みを利用することで、Titanium にない機能を Web サービスで補ったり、サービスから端末固有の機能にアクセスしたりできる。ネイティブアプリのブラウザ コントロール ( Android = WebView、iOS = UIWebView ) でも同様の仕組みを用意しているけれど、Titanium の場合、それを扱う部分を一元化できる。
リソース HTML の表示
WebView の url プロパティには Titanium プロジェクト内のリソース HTML を指定できる。
パスのルートは Resources ディレクトリとなり、URL の指定は相対パスでよい。ただし Titanium の JavaScript とリソース HTML から読むものを区別する意味で、専用のサブディレクトリを設けたほうがよさそうだ。
例えば Resouces の下に web という名前のフォルダを用意するとして、その中の index.html を表示する場合の処理は、以下のようになる。
var window = Titanium.UI.createWindow();
var webView = Titanium.UI.createWebView( { url: "web/index.html" });
window.add( webView );
window.open();
リソース HTML を利用するならオフラインでもよいので、アプリの GUI を Web ベースで実装したいときや、ヘルプのような資料を表示する場合に役立つ。
サンプル アプリ
前にこのブログで書いた Titanium と Aptana でモバイル開発ではマップを操作するサンプルを作成したが、今回は Web サービスの Google Maps API V3 を WebView でホストして同等の機能を実現してみた。マップ読み込みはリソース HTML + JavaScript でおこなっている。
Titanium のプロジェクトは build ディレクトリも含めないと不完全なのだが、サイズが大きすぎるのでやめておく。代わりに Resources ディレクトリ内を ZIP 圧縮したものを公開する。
TestWebView.zip
動作を確認する場合は、Titanium 上で適当なプロジェクトを作成し、その中の Resouces ディレクトリをサンプルのものに置き換える。ただしサンプルの Resources には iphone 向けのデータを含んでいないため、Mac OS X + iOS SDK な環境で試すなら、元の Resouces に入っている iphone というディレクトリの中身を流用する。
WebView のバグ
Titanium の WebView でリソース HTML を利用してみたとろ、バグがあることに気づいたので記録しておく。
バグ 1 : ページ全面を覆う div で Google Map を表示できない
問題が発生すると以下の画像のような状態となる。
サンプルアプリを動かすと WebView 部分に縦スクロールバーが表示されているが、それはこの問題を回避するための CSS 指定によるものである。サンプルアプリの CSS は以下のようになっている。
@charset "utf-8";
html, body {
height: 100%;
margin: 0px;
}
#map_canvas {
height: 100%;
margin:0px 1px 1px 0px;
}
10 行目が対策部分となる。縦横 1 ピクセルの余白を入れ、全面表示されないようにしている。CSS で overflow:hidden を指定すればスクロールバーを消せるが、そうすると問題が再現する。JavaScript で動的に指定してもダメだった。HTML や CSS をいろいろ変更してみたが、全面表示かつスクロールバーなしは実現できてない。識者のかた、意見を求む。
この問題は Titanium の WebView + Android 限定らしく、ネイティブな Android プロジェクトの assets に件のリソースを追加して WebView へ指定したが、再現しなかった。また、Titanium でも iPhone の場合は発生しなかった。
バグ 2 : iPhone でリソース HTML を利用した場合、history が記録されない
この問題は jQuery Mobile を試していて気がついた。jQuery Mobile では以下のような定義を特別なページとして扱い、それが遷移先だった場合は、ヘッダ部分へ自動的に Back ボタンをつけてくれる。
<div data-role="page"> <div data-role="header"> <h1>タイトル</h1> </div> <div data-role="content"> ページの内容 </div> </div>
通常は Back ボタンを押したときに元のページへ戻るのだが、以下の条件が揃うと動作しない。
- Titanium Mobile の WebView で表示している
- ページは Web 上ではなくリソース HTML
- iPhone 上で動作している
はじめはタップがうまく処理できていないのか、Back ボタン絡みでスクリプトエラーになっているかと思ったが、PC の Firebug で動作確認してみたところ、このボタンが押されたときは以下のコード ( jQuery Mobile 1.0 Alpha 2 のものとなる ) が実行される。
$( "<a href='#' class='ui-btn-left' data-icon='arrow-l'>"+ o.backBtnText +"</a>" )
.click(function() {
history.back();
return false;
})
history はページ遷移を記録するオブジェクトで、ブラウザの Back/Forward 操作を JavaScript からおこなう時に利用する。history.back() は Back 操作に相当する。もちろん、iPhone のブラウザもサポートしているし、Titanium の WebView に指定する先がオンラインならバッチリ動作する。
ではなぜリソース HTML だと動かないのか。history オブジェクトは管理している履歴数を length プロパティに持っているので表示してみたところ、リソース HTML の場合はいくらページ遷移しても 1 のまま増加しない。どうやら履歴を記録できていないようだ。
対策を考えてみたが、うまい方法が思い浮かばない。WebView 連携を知ったとき、jQuery Mobile を GUI に使えるのでは?と思ったのだが、この問題により断念している。


ピンバック: Tweets that mention Titanium の WebView で HTML をホスト | アカベコマイリ -- Topsy.com
ピンバック: [Titanium Mobile] WebViewのリソースHTML » TECH Matari
ピンバック: #Titanium mobile で開発しだしたら役に立つ情報のまとめ | astronaughts.net
受け渡しするオブジェクトは途中でJSONに変換されるので、循環があるオブジェクトやfunctionオブジェクトを渡そうとするとエラーを起こします。デバッガは何も言ってくれませんが。