Titanium+android環境で、複数タブにマップを表示させるサンプルコード【ただし似非】

titaniumでのandroid対応の難易度がいちいち高い件

titaniumでのandroid実装が予想外に骨が折れて涙目の毎日です、こんにちは。

経験した人もちらほらいると思っているのですが、titaniumのmapViewを複数のwindowコンテキストで生成してwindowに追加すると「androidだけ」エラーがでます。
iOSはそういう実装でも普通に動きます。
多分、androidでmapを呼ぶ場合、普通のActivityではなくMapActivityを継承するという点と、titaniumのmapAPIのproxy方法がなにか悪い科学反応起こしてる気がしているのですが、詳しいことはソースよく見てないのでワケワカメです。ヘ(゚∀゚ヘ)アヒャ


とりあえず、似非でtitanium☓androidでも複数(っぽく見える)マップ表示っぽい何かをやってみましたので、サンプル上げてみます。

サンプルコードについて

  • 動作は、android2.3の実機でのみ確認しています。(XperiaArcで確認しました)
    • iPhoneシミュレータ上では多分動いてくれない気がします。(こんな構造にする必要ないし)
  • サンプルはタブグループを使い、各ウィンドウに配置したボタンを使って地図のオンオフをして複数ウィンドウでも(見た目だけ)マップ使えますよアピールをしています。
    • もし、ボタン等を使わずに、タブが切り替わった段階で地図のオンオフを切り替えたい場合は、どうにかしてタブの切り替わりをblurとresumeイベントから拾うか、fireEventを呼んでマップウィンドウのオンオフをしたら良いと思います。(私は諸々あってタブグループの使用を諦め、オレオレタブグループを自作しました。)
      • タブグループを本位としたtitaniumにおいて、オレオレタブグループをやるのはかなり骨で涙目です、本当にありがとうございました。。。
実装ポイント

多分以下二つを約束して実装すると、androidでも複数(っぽく見える)マップができるんじゃないかという点

  • 1つのアプリケーションから呼んで良いマップオブジェクトは一つだけ
  • マップオブジェクトとウィンドウコンテキストの結びつきは1:1の関係を保つ

サンプルコード本体

app.js
// アプリにただひとつのマップクラスオブジェクト的なものを作成します。
Ti.include('map_util.js');
Ti.App.myMap = new mapUtil();


var tabGroup = Titanium.UI.createTabGroup();


// win1
var win1 = Titanium.UI.createWindow({  
    title:'Tab 1',
    backgroundColor:'#fff',
    url:'win1.js'
});
var tab1 = Titanium.UI.createTab({  
    icon:'KS_nav_views.png',
    title:'Tab 1',
    window:win1
});

// win2
var win2 = Titanium.UI.createWindow({  
    title:'Tab 2',
    backgroundColor:'#fff',
    url:'win2.js'
});
var tab2 = Titanium.UI.createTab({  
    icon:'KS_nav_ui.png',
    title:'Tab 2',
    window:win2
});


tabGroup.addTab(tab1);  
tabGroup.addTab(tab2);  


// open tab group
tabGroup.open();
map_util.js
/**
 * 地図クラス
 *
 * このオブジェクトを複数生成し、同時にwindowをopenするとアプリが落ちるので注意。
 *(androidのみ。iPhoneはこのような構造をもつ事自体必要ありません)
 */
var mapUtil = function() {
    this.mapWindow = Ti.UI.createWindow({  
	title:'map',
	backgroundColor:'#999',
	width:300,
	height:300,
	top:60
    });

    // 仮に日本のへそっぽいところに座標もってきてます。適当にジオロケータから値をとって更新してください。
    this.mapView = Titanium.Map.createView({
	mapType: Titanium.Map.STANDARD_TYPE,
	region: {latitude:35.000000, longitude:135.000000, latitudeDelta:0.00, longitudeDelta:0.00},
	animate:true,
	regionFit:true
    });

    this.isMap = false; // mapが使われているかどうか判別するために。
    this.currentPin = null; // マーカーなど必要であれば適当な形でメンバにもっておくと良いかもしれません。

    this.mapWindow.add(this.mapView);
};


// マップ用メソッドはこちら。
mapUtil.prototype = {
    // 地図ウィンドウを開く
    open:function(){ 
	if(this.isMap){this.mapWindow.close();}
	this.mapWindow.open();
    this.isMap = true;
    },
    //  地図ウィンドウを閉じる
    close:function(){
	if(this.isMap){
	    this.mapWindow.close();
	    this.isMap = false;
        }
    }
};
win1.js
/**
 * win1
 */

var win = Ti.UI.currentWindow;

var label = Titanium.UI.createLabel({
	color:'#999',
	text:'I am Window 1',
	font:{fontSize:20,fontFamily:'Helvetica Neue'},
	textAlign:'center',
	width:'auto',
    top:0,
    left:10
});


var button_showMap = Ti.UI.createButton({
    width:100,
    height:60,
    title:'map Show',
    bottom:10,
    left:0
    });

button_showMap.addEventListener('click',function(){
Ti.App.myMap.open();
});

var button_hideMap = Ti.UI.createButton({
    width:100,
    height:60,
    title:'map Hide',
    bottom:10,
    left:110
    });

button_hideMap.addEventListener('click',function(){
Ti.App.myMap.close();
});


win.add(label);
win.add(button_showMap);
win.add(button_hideMap);

win2.jsはwin1.jsの丸コピなので割愛。


マップウィンドウはクラスオブジェクトにまとめているので、適当にメソッドを追加して、各ウィンドウで必要な形で表示できるようにカスタマイズしたら良いと思います。
あとは、同じようにジオロケータをクラスにまとめてapp.jsでnewしてTi.Appにくっつけておくと、まあウマーではないかと。


titanium使ってまで、androidオンリーの実装書いてる時点ですでに激不味いというのは自覚がありますので、スルーでお願いします。
結局iPhone→titanium,androidjavaの実装が最強ではないかという疑惑でさようなら。