Androidとクロスプラットフォームの開発だとどうすればいいか悩むので、ここにメモしておきます。
例えば、NavigationWindowを使ってListViewとCollectionのバインディングを実現したい場合。
iOSのレイアウトは簡単にこんなのだとします:
<Alloy>
<Collection src="entry" />
<NavigationWindow platform="ios">
<Window>
<ListView>
<Templates>
<ItemTemplate name="entry">
<Label class="titleLabel" bindId="title" />
<Label class="subtitleLabel" bindId="subtitle" />
</ItemTemplate>
<ListSection dataCollection="entry" dataTransform="dataTransform">
<ListItem template="entry" titleLabel:text="{title}" subtitleLabel:text="{subtitle}" />
</ListSection>
</Templates>
</ListView>
</Window>
</NavigationWindow>
</Alloy>
単純ですね。一方、処理を担当するコントローラはこんな感じ。
var collection = Alloy.Collections.entry;
function dataTransform(model){
var transform = model.toJSON();
return transform;
}
function loadData(){
collection.fetch({
//オプションとか
});
}
$.listEntries.addEventListener('open', function(e){
loadData();
});
iOSでビルドするとめでたしめでたし、ちゃんと動作するのですが、Androidだと何も表示されません。というかWindowがないのでエラーになります。どうして?
だってWindowはNavigationWindowの中にあるから、Alloyのビルド時にplatform属性でAndroidの場合は中身は全部捨てられてしまんですもの。
いちばん単純な解決方法は、app/views/androidとapp/views/iosディレクトリを作って、それぞれにXMLを用意することです。iOS用にはNavigationWindowを使い、Androidでは使わない。単純です。欠点は、変更の際に両方のファイルに反映させないといけないことで、まあそういう雑な運用は大抵破綻するので、遠くない将来に必ずうっかり不具合を仕込むことになります。
問題なのはCollectionがAlloyの直下になければいけない点です。この制限さえなければ、もっと単純にできるのに。。。
ところが、うまい具合に抜け穴があったので紹介します。ここからが本題ですね。
というわけで、こんな風にしてみます。まず、iOS専用のlistEntriesNav.xmlを用意します。
<Alloy>
<NavigationWindow platform="ios">
<Window>
</Window>
</NavigationWindow>
</Alloy>
ダミーのWindowがないとコンパイルエラーになります。
次に、listEntries.xmlとして実際のWindowのレイアウトを作ります。まあ、さっきとほぼ同じですね。
<Alloy>
<Collection src="entry" />
<Window>
<ListView>
<Templates>
<ItemTemplate name="entry">
<Label class="titleLabel" bindId="title" />
<Label class="subtitleLabel" bindId="subtitle" />
</ItemTemplate>
<ListSection dataCollection="entry" dataTransform="dataTransform">
<ListItem template="entry" titleLabel:text="{title}" subtitleLabel:text="{subtitle}" />
</ListSection>
</Templates>
</ListView>
</Window>
</Alloy>
NavigationWindowだけが消されています。
次にコントローラにも細工をします。基本的には同じですが、openイベントが発火しないかもしれないのでそこだけ変更します。
//$.listEntries.addEventListener('open', function(e){
// loadData();
//});
exports.loadData = loadData;
exportsを使って、コントローラの呼び出し元からloadData関数を実行できるようにしてあげます。これで準備完了です。
あとはそれぞれのプラットフォームに応じてNavigationWindowを開いてWindowを入れ替えるか、そのままWindowを開くか決めて、実行します。
var nav;
var win = Alloy.createController('listEntries');
if(IOS){//こういうのがあると便利ですよね
nav = Alloy.createController('listEntriesNav').getView();
nav.window = win.getView();
}else{
nav = win.getView();
}
nav.addEventListener('open', win.loadData);
nav.open();
ようするに、まずNavigationWindowを用意しておいて、その中身はだたのWindowオブジェクトでいいのだから、データバインディングとかはそっちの方でやっているというだけですね。
これで、iOSとAndroidでNavigationWindowを使っていてもコードを共有しながらAlloyのデータバインディングを実現することができました。めでたしめでたし。