Titanium™とは?
Appcelerator社が提供している、JavaScriptでiOS/Android のネイティブアプリを開発することができる開発環境です。第1回は基本のJavaScriptのみで記述していくクラシックスタイルで簡単なTODOアプリを作成しながら始めます。ひと通りTitaniumの基本を身につけられるかと思います。
Titanium APIはiOS/AndroidのAPIと橋渡しをしてくれるため、Titanium APIが対応していればiOS/AndroidのネイティブAPIをフルに活用することが可能となります。Titaniumで利用できるAPIは、開発元のAppceleratorが提供しているお手本アプリ、Kitchen Sinkをご覧ください。全て英語ですがオンラインのリファレンスマニュアルも提供されていますので、あわせてご活用ください。
Alloy・TCSとは?
第2回でAppcelerator社公式フレームワークであるAlloyを用いたモダンな開発スタイルへTODOアプリをポーティングするところまでを解説します。
AlloyとはAppcelerator社公式のMVCフレームワークとなります。コントローラとモデルはJavaScriptで、ビューはXML、ビューの修飾はCSSと似たようなTSS(Titanium Style Sheets)で記述していきます。Titaniumはデータの保存にSQLiteが使えます。第2回まではSQLiteを使いますが、第3回でAppcelerator社が提供するmBaaSであるTitanium Cloud Service(TCS)へデータを保存するところまでをゴールとして解説していきます。
Alloyは内部でBackbone.jsが使われております。モデルはそのままBackbone.jsの概念が使えますので、第1期の野村亮之さんが執筆された「JavaScriptでクライアントMVCに挑戦!」を参考にしてください。
また、mBaaSであるTCSの概念につきましても第1期で鈴木理恵子さんが執筆された、「サーバーサイドからさようなら。BaaS入門。」を参考にしてください。
Appcelerator社が提供する統合開発環境Titanium Studioの構築は、ガイドサイトを用意しましたので、是非そちらをご覧ください。
全3回で以下の様な内容となり、HTML/CSS/JavaScriptをある程度理解している、ウェブエンジニアとウェブデザイナの方が対象となります。
[第1回] Titaniumの基本を学ぶ、JavaScriptだけのクラシックスタイルでTODOアプリを開発しよう
[第2回] Appcelerator社公式フレームワーク、AlloyでTODOアプリを再構築してみよう
[第3回] Appcelerator社が提供するmBaaS、Titanium Cloud Service(TCS)にTODOアプリのデータを保存しよう
利用シーン
ツール系のアプリであれば、Titaniumで十分に開発することができると思います。その反面、瞬時にレスポンスを求められるようなアプリ、例えば、ゲームの開発を苦手とします。開発したいアプリの性質を見極めて開発ツールを選択すると良いでしょう。
Titanium API
ソースの中に出てくるTitanium.XXXやTi.XXXがTitanium APIとなります。TiはTitaniumの省略形です。色々なソースコードがGitHub等で公開されていますが、Titanium APIの記述は長くなりがちなのでTi.XXXが好まれているようです。どちらで記述しても構いませんが、今回はTi.XXXの方で説明します。
注意事項
Titaniumはよくある「Write once, run anywhere」ではありません。プラットフォームごとにコードを書き分ける必要があります。「Write once, adapt anywhere」ということを覚えておきましょう。
アプリを作ってみよう
簡単なTODOアプリを私のGitHubレポジトリに用意しておきました。まずはこちらをダウンロードして動かしてみてください。ダウンロードした.zipファイルを適当な場所で解凍します。Titanium StudioのFile→ImportからTitaniumのExisting Mobile Projectを選択し、Project directoryで解凍したフォルダを選択してください。これでTitanium Studioでこのアプリを扱うことができるようになります。
起動できましたか?一目見て見た目が異なっていることがわかるかと思います。iOSの場合は「未完了/完了/全て」のフィルタが画面下部に表示されていますが、Androidの場合はアクションバーへ入っています。また、挙動も大きく異なります。タスクのリストを下へ引っ張ってみましょう。iOSではメールアプリのようなPull-To-RefreshのUIパーツがサポートされていますが、Androidにはありません。このように、iOSではできることがAndroidではできなかったりします。
TODOアプリの操作方法
- リストの一番上は新しくタスクを登録することができます
- 一番下にあるメニューでタスクの表示を切り替えることができます
- タスクを左右どちらかにスワイプすると完了、もしは未完了へ戻すことができます
- タスクを長押しすると削除することができます
ファイル構造
Resources配下にアプリのソースが格納されています。app.jsがエントリーポイントとなり、Titanium製のアプリはここから処理が実行されます。
app.js
app.jsは初回起動時のデータベースの生成と、iOS/Androidの処理の振り分けを行っています。普段ブラウザのJavaScriptを書かれている方には見慣れないrequire
がありますが、これはnode.jsではお馴染みの外部ファイルのモジュールを読み込むために使います。このように、Titaniumではrequire
がサポートされていることにより、画面やUIパーツをモジュール単位で定義して使い回しやすくなっています。
1 2 3 4 5 6 7 8 | var Window; // iOS/Androidで処理を振り分け if (Ti.Platform.osname === 'android' ) { Window = require( '/ui/handheld/android/ApplicationWindow' ); } else { Window = require( '/ui/handheld/ApplicationWindow' ); } new Window().open(); |
Titaniumを少しでも触ったことがある方は、Ti.include
でも良いかと思うかもしれませんが、こちらはrequire
と別物です。決定的な違いとして、Ti.include
は変数のネームスペースを汚染してします。これはビルド時にソースコードをすべて結合してしまうため、全てがグローバルな変数になってしまうためです。require
は、都度読み込むためそのようなことが起こりません。必ずrequire
で外部ファイルを読み込むようにしましょう。
ApplicationWindow.js
app.jsのrequire
の通り、iOS/AndroidでApplicationWindow.jsの場所が異なりますのでご注意ください。こちらは大元となるウィンドウを生成し、その中に格納するUIパーツをrequire
で別ファイルから読み込んでいます。
require
で読み込まれて提供されるのはmodule.exports
で定義されているApplicationWindow()
という関数になります。ApplicationWindow()
はTi.UI.iOS.NavigationWindow
がreturn
で返却されています。つまり、var Window = require(/ui/handheld/ApplicationWindow);
はTi.UI.iOS.NavigationWindow
が返却されるため、ウィンドウを開くWindow.open()
メソッドが使えるということになります。
iOSの場合
iOSの場合は画面上部のナビゲーションバーを生成するため、Ti.UI.iOS.NavigationWindow
にウィンドウを格納します。少々わかりづらいですが、最新のTitanium SDKではTi.UI.iOS.NavigationWindow
を使わないとナビゲーションバーを生成することができないことを覚えておいてください。
1 2 3 4 5 6 7 8 9 10 | // ウィンドウを生成 var win = Ti.UI.createWindow({ backgroundColor: '#fff' , title: 'myTODO' }); // iOS用の大元となるウィンドウを生成 var self = Ti.UI.iOS.createNavigationWindow({ window: win }); |
Androidの場合
Androidの場合は少々複雑になります。まず、Androidではバックボタンがハードウェアで用意されていることを思い出してください。exitOnClose
プロパティはバックボタンが押された時アプリを終了するために指定しています。これを記述していないでバックボタンを押すと、なんとスプラッシュスクリーンに戻ってしまいます。windowSoftInputMode
はソフトキーボードの制御に使われます。今回は入力フィールドをタップした時に画面をうまく調整してくれるようにTi.UI.Android.SOFT_INPUT_ADJUST_PAN
を指定してあります。また、self.activity.onCreateOptionsMenu = function(){};
でアクションバーに配置されているボタンを定義してあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // ウィンドウを生成 var self = Ti.UI.createWindow({ backgroundColor: '#ffffff' , title: 'myTODO' , windowSoftInputMode: Ti.UI.Android.SOFT_INPUT_ADJUST_PAN, exitOnClose: true }); // アクションバーのボタンを生成 self.activity.onCreateOptionsMenu = function (e){ var incomplete = e.menu.add({ title: '未完了' , showAsAction: Ti.Android.SHOW_AS_ACTION_ALWAYS }); incomplete.addEventListener( 'click' , function (){ // 画面を描画 firstView.fireEvent( 'rendering' , { display: 'incomplete' }); }); }; |
FirstView.js
こちらもプラットフォームごとに異なる場所に設置していますのでご注意ください。中身はタスクリストの表示のためのTi.UI.TableView
が定義してあります。また、タスクの登録や操作用のイベントが定義されています。iOSの場合は、Pull-To-Refresh用のTi.UI.RefreshControl
が定義されていたりしますので、是非AndroidのFirstView.jsと見比べてみてください。
アプリは基本的にイベント駆動になります。UIパーツにイベントをaddEventListener
で定義して、タップやスワイプした時の挙動を記述していきます。また、ApplicationWindow.jsからFirstView.jsに対して、firstView.fireEvent('rendering', {})
されているのがそうであるようにカスタムのイベントを登録することができます。このようにrequire
でソースが分断されてしまっても、イベントを伝搬させることができます。
renderingイベントですが、なぜこのようなことをしているのかというと、UIパーツをすぐに返却してできるだけ画面のレスポンスを向上させるためが一点目。さらにタスクリストの再描画は頻繁に使うため、使い回しが効くようにしてあります。つまり、ユーザの待ち時間を減らすことと、再利用のためにイベントとして登録してあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | // 画面の描画イベント self.addEventListener( 'rendering' , function (e){ // 描画開始 control.beginRefreshing(); // タスクリストの格納するための配列を初期化 var rows = []; // タスク登録フィールドを配列へ追加 rows.push(createAddRow(self)); // データベースからタスクリストを取得 var done = '' ; display = e.display || display; switch (display) { case 'incomplete' : done = 'AND done = 0 ' ; break ; case 'complete' : done = 'AND done = 1 ' ; break ; } var db = Ti.Database.open( 'myTODO' ); var result = db.execute( 'SELECT id, task, done, created_at ' + 'FROM todos ' + 'WHERE deleted_at = \'0000-00-00 00:00:00\' ' + done + 'ORDER BY id ASC' ); while (result.isValidRow()) { // タスクリストを配列へ追加 rows.push(createTaskRow(result)); result.next(); } result.close(); db.close(); // テーブルビューへタスクリストをセット tableView.setData(rows); // 描画完了 control.endRefreshing(); }); |
最後に
内部的な処理自体は同じでも、iOS/AndroidでUIパーツで使える・使えないAPI(のプロパティ)が存在することがわかりましたか?キャンセルボタンの他にもご自身で色を変えたり画像を追加してみたりと、色々お試しください。
主観ですがTitaniumでアプリを開発する場合、基本的にAndroidの方が制限が多いように感じます。私の開発スタイルですが、まずはAndroidで基本部分を実装してからiOSの開発に着手すると、両対応がしやすく感じます。もちろんプラットフォーム毎にUI設計の作法がありますので、見た目を同じにすることはオススメできません。「Write once, adapt anywhere」を忘れずに開発しましょう。
また、全てJavaScriptで記述していきますので、UIパーツが増えていった際にソースコードがとても長くなり、とても見づらいものになってしまいます。これでは中・大規模なアプリになるとさすがに開発やメンテナンスが厳しくなってしまうでしょう。
第2回目はこのTODOアプリをAlloyで再構築し直します。UIパーツの見栄えの部分が別ファイルになったり、UIパーツ自体の定義も別ファイルになり、JavaScript部分はロジックの実装のみになっていきます。今回のコードより、WebアプリのようなHTML/CSS/JavaScirptの構成に近くなり皆さんには馴染みのやすいかと思いますので、ぜひご期待ください。