Titanium の WebView で HTML をホスト
Titanium の WebView で HTML をホストする方法の覚え書き。
WebView
WebView は Titanium.UI.WebView として定義される Webブラウザ コントロールである。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 圧縮したものを公開する。
動作を確認する場合は 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;
}
縦横 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 ボタン絡みでスクリプト エラーになっているかと予想していた。しかし 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
はページ遷移を記録するオブジェクトで Web ブラウザーの Back/Forward 操作を JavaScript からおこなう時に利用する。history.back()
は Back 操作に相当する。もちろん iPhone Safari もサポートしているし Titanium の WebView
に指定する先がオンラインならバッチリ動作する。
ではなぜリソース HTML だと動かないのか。history
オブジェクト履歴数を length
プロパティに持っているので表示してみたところ、リソース HTML の場合はいくらページ遷移しても 1 のまま増加しない。どうやら履歴を記録できていないようだ。
対策を考えてみたが、うまい方法が思い浮かばない。WebView
連携を知ったとき jQuery Mobile を GUI に使えるのでは?と思ったのだが、この問題により断念している。