Titanium/Alloyでマルチプラットフォーム向けにアプリを作る際、以下のような問題があります。
Windowの中身を二度書くのがいやなのと、中身を共通化してrequireしようとするとdataCollectionがちゃんと動かないんですよねえ。
- Titanium - NavigationWindowでもAlloyのCollectionを使いたいしAndroidとコードを共有したい - Qiita
iPhoneでナビゲーションバーを使うにはNavigationWindowが必要ですが、Androidにはないコンポーネントなので、ビューを作るのに一工夫必要になってしまうのですが、そうするとデータバインディングと相性が悪いらしいという悩ましい問題です。
上の記事では、コントローラ内でiOSかAndroidを判断して、Androidの場合はWindowをそのまま使い、iOSの場合はNavigationWindowを生成してWindowを挿入するような形で対応する方法を紹介されていました。
正攻法で行くなら、冗長になってしまいますが、上記記事のコメントにあるようにiOSとAndroidそれぞれにWindowを書く形、
<Alloy>
<Collection src="entry" />
<NavigatonWindow platfrom="ios">
</NavigationWindow>
<Window platfrom="android">
</Window>
</Alloy>
もしくは app/views/
以下にプラットフォームごとのフォルダを用意して、それぞれのViewを実装する形でしょうか。
データバインディングを使わないのであれば、 require
を使ってわかりやすく共通化できるのですが。
参考: iOSとAndroidのナビゲーションサンプル | garicchi.com
そこで、別のアプローチとして、私が普段Titanium開発をする際に使っているViewをJadeで書く(&TSSをStylusで書く)方式での解決方法を紹介したいと思います。
参考: Titanium Alloy with StylusでFontを設定 - Umi Uyuraのブログ
XP.UI.js(Jadeを使わない方法)
と言いつつ、先にJadeを使わない場合の解決のひとつを紹介。
Titanium/Alloy向けの便利 FokkeZB/UTiL に含まれている xp.ui.js を使う方法です。
詳細は リポジトリのドキュメント に詳しく書かれていますが、iOS以外のプラットフォーム用にダミーのNavigationWindowを提供しているので、以下のように module="xp.ui"
を設定することで、iOS/Android両方に対応できます。
<Alloy>
<NavigationWindow module="xp.ui">
<Window>
<Label>Hello World</Label>
</Window>
</NavigationWindow>
</Alloy>
先日Alloy Modelの勉強していたときに使ってみたので、データバインディングと組み合わせも問題なさそうです。
Alloy Modelの学習 - Umi Uyuraのブログ
Jade版
Jadeでviewを拡張する方法としては、 include
extend
mixin
などがありますが、このうち mixin
を使うことにしました。
mixin
は、再利用可能なコードブロックを作って、任意の箇所で何度も生成できる機能です。関数のように引数を取ることもできます。
それを利用して、まずは mixin
で基本となる画面をWindowベースで作りました。引数でプラットフォームの指定を受け取るようにしたので、生成される <Window>
タグは各プラットフォーム専用になります。
それを、iOS用のNavigationWindow以下と、Android用にAlloyタグ配下の2箇所で呼び出すようにしました。
app/views/index.jade
mixin mainWindow(platform)
Window.container(platform=platform, title="Alloy Project Demo", onOpen="doOpen", onClose="doClose")
ListView(onItemclick="clickItem")
ListSection(dataCollection="member")
ListItem(title="{name}", itemId="{id}")/
Alloy
Collection(src="member")/
NavigationWindow(platform="ios")
+mainWindow("ios")
+mainWindow("android")
これをJadeコンパイルすると、以下のようなView XMLが生成されます。
app/views/index.xml
<Alloy>
<Collection src="member"/>
<NavigationWindow platform="ios">
<Window platform="ios" title="Alloy Project Demo" onOpen="doOpen" onClose="doClose" class="container">
<ListView onItemclick="clickItem">
<ListSection dataCollection="member">
<ListItem title="{name}" itemId="{id}"/>
</ListSection>
</ListView>
</Window>
</NavigationWindow>
<Window platform="android" title="Alloy Project Demo" onOpen="doOpen" onClose="doClose" class="container">
<ListView onItemclick="clickItem">
<ListSection dataCollection="member">
<ListItem title="{name}" itemId="{id}"/>
</ListSection>
</ListView>
</Window>
</Alloy>
冒頭に正攻法として紹介した、iOSとAndroidそれぞれに向けて二重にWindow以下を書いた形で出力されました。当然データバインディングも問題なく動きます。
まとめ
Jadeを使うことで、Windowの中身を1箇所で書くことができ、煩雑さは多少減らせるのではないかな、と思います。
Jade版でのサンプルをGitHubにあげてあります。
umi-uyura/AlloyModelWithJadeNavWin
ちなみに、サンプルはJadeおよびStylusから生成したView XMLとTSSも含んでいるので、Jade & Stylusがない環境の人でも、alloy.jmkを削除して (appc) ti build
を実行してもらえば動きます。