Androidアプリで,Google Maps API+GPS+Geocoderを使って,現在地の地図と地名を表示させよう
重要なお知らせ:
この記事で公開した情報は,AndroidのMVCフレームワーク「Android-MVC」の機能の一部として取り込まれました。
より正確な設計情報や,動作可能な全ソースコードを閲覧したい場合,「Android-MVC」の公式ページより技術情報を参照してください。
AndroidのMVCフレームワーク - 「Android-MVC」
http://code.google.com/p/android-mvc-...
AndroidはGoogleのOSなので,Google Mapsを使った地図アプリを作るのも簡単。
ここでは,GoogleMapを表示するだけの簡単なアプリからスタートし,
その後GPSやGeoCoder(地名変換ライブラリ)と連携したアプリを作る。
実機が無くても,エミュレータだけで無料でテストできる。
手順:
- (1)証明書のフィンガープリントを取得
- (2)API Keyを取得
- (3)Androidアプリ内でGoogle Mapsを利用
- (4)GPSとGeocoderを利用するように作り替え
- 補足1:今後必要になる情報
- 補足2:Graphical Editor上でMapViewを表示
- 補足3:マーケット公開用のアプリでMaps APIを利用したい場合
事前の準備:
今から1時間で,Androidアプリの開発環境を構築し,Windows上でサンプルを動作させる手順
http://language-and-engineering.hatenablog.jp/entry/20110724/p1
(1)証明書のフィンガープリントを取得
Windows Vista上で,コマンドプロンプトから:
C:\Windows\system32>where keytool C:\Program Files\Java\jdk1.6.0_16\bin\keytool.exe C:\Windows\system32> keytool -list -keystore C:\Users\(ユーザ名)\.android\debug.keystore キーストアのパスワードを入力してください:(エンターキーのみ押下) ***************** 警告 警告 警告 ***************** * キーストアに保存された情報の完全性は検証されて * * いません! 完全性を検証するには、キーストアの * * パスワードを入力する必要があります。 * ***************** 警告 警告 警告 ***************** キーストアのタイプ: JKS キーストアのプロバイダ: SUN キーストアには 1 エントリが含まれます。 androiddebugkey, (日付), PrivateKeyEntry, 証明書のフィンガープリント (MD5): (フィンガープリントの内容)
この,フィンガープリントの内容をメモして控えておく。
keytoolについて参考:
keytool - 鍵と証明書の管理ツール
http://java.sun.com/j2se/1.4/ja/docs/...
- 非公開鍵、および対応する公開鍵を認証する X.509 証明書が格納されたキーストア (データベース) を管理
- keytool は、鍵と証明書を「キーストア」に格納します。 デフォルトのキーストアの実装は、キーストアをファイルとして実装しています。 キーストアは、非公開鍵をパスワードで保護します
- keytool ツールと jarsigner ツールは、JDK 1.1 で提供されていた javakey ツールを完全に置き換える
- -keystore オプションは、keytool で管理するキーストアに対応する永続的なキーストアファイルの名前と場所を指定
Java/keytool 全オプションの使い方
http://apis.jpn.ph/fswiki/wiki.cgi?pa...
Javaのキーストアに証明書を追加する
http://d.hatena.ne.jp/uunfo/20110813/...
JKSとSSL証明書について参考:
証明書の変換(gkrからjks)
http://blogs.yahoo.co.jp/chototsu_mou...
- デジタル証明書のキーストアの形式には2種類ある
- GNUのgkeytoolで作ったgkr形式
- SUNのkeytoolで作ったjks形式
Apache HarmonyのJSSE(Java Secure Socket Extension)
http://blog.livedoor.jp/k_urushima/ar...
- JKS形式はSun独自の形式なので,HTTPSのためのSSLサーバー証明書と鍵には使えない
- HTTPSではPKCS#12かBKS(BouncyCastle Key Store)形式を使う。BKSがおすすめ
申込時に用意しなくてはいけないCSRファイルとは何でしょうか?
https://crosstrust.co.jp/support/faq/...
- 認証局に対し、SSLサーバ証明書への署名を申請する内容
SSL入門
http://www005.upp.so-net.ne.jp/nakaga...
- クライアントはサーバー証明書が取得できるようになっています。サーバー証明書は、公開鍵に、信頼できる機関である認証局(以下 CA)が署名したもの
- 身内が使うための通信路を秘密にしたい場合に自分で署名したサーバー証明書を 使うことはありますが、第三者に大丈夫だといって使わせるのはやめましょう。 それは、PKI としての意味をなしていない
- サーバー証明書ファイルを取得するためには、まず CA 署名してもらう CSRファイルを作成します。次に、 CSRファイルを CAに送って、署名してもらったものがサーバー証明書ファイル
- Webサーバーを自動的に起動したい場合,暗号化していない秘密鍵を置く。秘密鍵を盗まれないように厳重に注意
(2)API Keyを取得
Android Maps API Key Signup にアクセス。
契約文章を読み,
「I have read and agree with the terms and conditions」にチェック。
「My certificate's MD5 fingerprint:」に,フィンガープリントの内容を入力。
「Generate API Key」を押下。
「Android Maps APIキーにサインアップしていただき、ありがとうございます。」
の画面になる。
表示内容:
あなたのキーは次のとおりです: (API Keyの内容) このキーは、以下のフィンガープリントによる認証を使用した すべてのアプリケーションで有効です: (フィンガープリントの内容) 以下に、地図を活用するためのxmlレイアウトの例を示します: <com.google.android.maps.MapView android:layout_width="fill_parent" android:layout_height="fill_parent" android:apiKey="(API Keyの内容)" />
このAPI Keyの内容を大事に控えておく。
補足:
上記のAPI Key発行完了ページにおいて,「詳細については、APIのドキュメントを参照してください。」という文言で,下記のURLにリンクが張られているのだが,リンク先は404 Not Foundになるようだ。(2011年8月時点)
このリンク切れをGoogleに報告したいのだが,Developerとして問題をMailで報告するためにはGoogle Checkoutにサインインする必要があり,そのためにはクレジットカード情報などを入力する手間があったので,馬鹿馬鹿しくなり,やめておいた。
さらに,下記のページを開くと・・・
「報告されている問題のページに掲載されていない新しい問題を発見した場合、私達にお知らせください、調査いたします。」
というリンクがある。しかし,そのリンクを押しても,報告可能なページには遷移しない。
報告不可能である旨のメッセージも表示されない。
いずれもGoogleにしては珍しいバグ事例と言えるだろうか。
まあ,巨人なので,少々のほころびには目をつぶろう。
(3)Androidアプリ内でGoogle Mapsを利用
Eclipse上で,新規Androidプロジェクトを作成。
その際,ターゲットプラットフォームとして「Android *.*」ではなく,
該当するAndroid SDK バージョンに対応する「Google APIs」を選択することに注意。
※そうしないと,「MapActivityを型に解決できません」などのエラーに悩まされることになる。
もし,プロジェクトのターゲットを後から変更したい場合は,
パッケージエクスプローラ上でプロジェクトを右クリックし,
プロパティ→Android からターゲットを変更する。
レイアウト用のmain.xmlを見ると,LinearLayoutの中にHello, WorldのTextViewがある。
その下に,先ほどのXMLを張り付け。
<com.google.android.maps.MapView android:id="@+id/map_view1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:enabled="true" android:clickable="true" android:apiKey="(API Keyの内容)" />
GUIエディタ上からは各種属性をセットできないので,XML直打ちで済ませる。
※enabled属性とvlickable属性を入れ忘れると,地図上には日本列島が表示されたまま,操作を受け付けなくなってしまう。
※id属性を入れ忘れると,アクティビティ中からこの要素を参照できず,ズーム可能にできない。
※GUIエディタ上には「Failed to find style 'mapViewStyle' in current theme」というエラーメッセージが表示されるが,無視して構わない模様。
これでGoogle Mapsを呼び出すUIが完成。
次に,このAPI呼び出しを宣言・許可する。
AndroidManifest.xmlを開き,以下の2点を編集。
applicationタグ直下に,Google Mapsの呼び出し宣言を追記:
<uses-library android:name="com.google.android.maps" />
manifestタグ直下に,インターネットの利用許可を追記:
<uses-permission android:name="android.permission.INTERNET" />
次に,このUIを呼び出し可能にするため,アクティビティのコードに手を加える。
サンプルコード:
package com.example; import android.os.Bundle; import com.google.android.maps.MapActivity; public class GmaptestActivity extends MapActivity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); MapView map = (MapView)findViewById(R.id.map_view1); map.setBuiltInZoomControls(true); } @Override protected boolean isRouteDisplayed() { return false; } }
ポイントは3つ。
- com.google.android.maps.MapActivityをインポートし,継承する。
- マップ要素に対して setBuiltInZoomControls() を実行し,ズーム可能にする。この属性は,レイアウトXML中で静的に指定する事は出来ないようだ。
- isRouteDisplayed()を実装する。
参考:
Googleマップを使用するAndroidアプリを作成する
http://www.techfirm.co.jp/lab/android...
- Googleマップを使用するにはMapActivityを継承するクラスを作成する必要があります
InflateException on defining MapView in XML
http://groups.google.com/group/androi...
- only allowed to create a MapView inside a MapActivity.
- I decompiled the MapView Class and I found the following code in the constructor
if(context instanceof MapActivity) 〜
else
throw new IllegalArgumentException("MapViews can only be created inside instances of MapActivity.");
MapViewにズームボタンを設置する簡単な方法は?
http://android.roof-balcony.com/view/...
- MapView.getZoomControlsというファクトリメソッドはもはや非推奨
そして,このアプリを実行する。
※XMLを実行しないように注意。アクティビティクラスにフォーカスを移してから実行すること。
エミュレータ上で,Google Mapが表示される。
エミュレータ上でのGoogle Mapsの操作方法:
- マウスでドラッグすれば,上下左右に移動可能。
- マップ上のある地点をクリックすると,マップ下部にズームボタンが現れる。「+」クリックで拡大,「−」クリックで縮小
- 最大までズームしても,ストリートビューには切り替わらない。
ここまでの全体的な流れの参考:
AndroidでGoogle Mapsを使う最も簡単なサンプル
http://www.adamrocker.com/blog/230/an...
- 手順が前後しており,ややわかりづらい
MapViewを使用するために、Maps API Keyを取得する
http://www.android-group.jp/index.php...
- Keyは証明書毎に取得する必要がある。エミュレータの開発用の証明書用のキーと、実際のデバイスで実行されるときの証明書のキーが必要。キーは、今のところ、いくつでも取得可能
- デバッグ時の証明書は、開発PC毎に生成されているので、PC毎に取得
Android Wiki / Google Mapを表示する
http://wikiwiki.jp/android/?Google%20...
- MapActivityはAndroidでMapを表示するための追加ライブラリ(maps.jar)に含まれています
(4)GPSとGeocoderを利用するように作り替え
前項までの単純なアプリに,仕様を2つ追加してみよう。
- 仕様1:初期状態で,現在位置がズーム表示されるようにする。
- 仕様2:地図上で表示している位置の情報を,住所として常に表示する。
アクティビティクラス:
package com.example; import java.io.IOException; import android.app.Activity; import android.location.LocationManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.widget.TextView; import com.google.android.maps.GeoPoint; import com.google.android.maps.MapActivity; import com.google.android.maps.MapView; import com.google.android.maps.MyLocationOverlay; public class GmaptestActivity extends MapActivity { /** Called when the activity is first created. */ private MyLocationOverlay overlay = null; private MapView map = null; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); map = (MapView)findViewById(R.id.map_view1); map.setBuiltInZoomControls(true); // ズームする map.getController().setZoom(15); // 場所を説明するラベル final TextView tv = (TextView)findViewById(R.id.address_label); final Activity context = this; // ラベルを書き換えるためのハンドラ final Handler ui_handler = new Handler() { //@Override public void handleMessage(Message msg) { String str_address = msg.getData().get("str_address").toString(); tv.setText( str_address ); } }; // マップ上にオーバレイを定義 overlay = new MyLocationOverlay(getApplicationContext(), map); overlay.onProviderEnabled(LocationManager.GPS_PROVIDER); overlay.enableMyLocation(); // GPSの位置情報の変更を監視 overlay.runOnFirstFix(new Runnable() { @Override public void run() { // マップ上で新たな現在位置へ移動 GeoPoint g = overlay.getMyLocation(); map.getController().animateTo(g); map.getController().setCenter(g); // 場所名を文字列で取得する String str_address = null; try{ // 住所を取得 str_address = GeocodeManager.point2address( (double)(g.getLatitudeE6() / 1000000), (double)(g.getLongitudeE6() / 1000000), context ); } catch(IOException e) { str_address = "座標情報から住所へのデコードに失敗"; } // 住所をメッセージに持たせて // ハンドラにUIを書き換えさせる Message message = new Message(); Bundle bundle = new Bundle(); bundle.putString("str_address", str_address); message.setData(bundle); ui_handler.sendMessage(message); Log.d("GPS操作", "位置の更新を検知"); } }); // このオーバレイをマップ上に追加 map.getOverlays().add(overlay); map.invalidate(); } @Override protected boolean isRouteDisplayed() { return false; } @Override protected void onDestroy() { // 破棄 overlay.disableMyLocation(); map.getOverlays().remove(overlay); super.onDestroy(); } }
座標と住所を変換するクラス
package com.example; import java.io.IOException; import java.util.List; import java.util.Locale; import android.content.Context; import android.location.Address; import android.location.Geocoder; public class GeocodeManager { // 座標から住所文字列へ変換 public static String point2address(double latitude, double longitude, Context context) throws IOException { String address_string = new String(); // 変換実行 Geocoder coder = new Geocoder(context, Locale.JAPAN); List<Address> list_address = coder.getFromLocation(latitude, longitude, 1); if (!list_address.isEmpty()){ // 変換成功時は,最初の変換候補を取得 Address address = list_address.get(0); StringBuffer sb = new StringBuffer(); // adressの大区分から小区分までを改行で全結合 String s; for (int i = 0; (s = address.getAddressLine(i)) != null; i++){ sb.append( s + "\n" ); } address_string = sb.toString(); } return address_string; } }
AndroidManifest.xmlのインターネット接続許可部に追記:
<!-- アプリケーションがGPSへアクセスするのを許可 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <!-- アプリケーションがテスト用の位置情報にアクセスするのを許可 --> <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
そして,レイアウト中でMapの上にあるTexiViewに,address_labelというidを付与。
これで,GPS情報が更新されるたびにマップが移動し,
該当する地名が画面上部に表示される。
なお,エミュレータ上では,GPS情報は手動で毎回,疑似的に「送信」する必要がある。
以下はその方法。
まず,エミュレータが立ちあがった状態で
ウィンドウ>パースペクティブを開く>DDMS
でDDMSを表示。
左側の「Dvices」ビューから,該当エミュレータを選択。
その下に「Emulator Control」というビューがあり,
下のほうに「Location Control」がある。
そこに,Longitude(経度)とLatitude(緯度)を入力し,
「送信」ボタンを押下すれば,
起動中のエミュレータに対して位置情報が送信される。
緯度と経度は,下記のサイトで調べればよい。
Geocoding.jp
http://www.geocoding.jp/
- 住所を入力すると,該当する座標の緯度と経度を表示
例えば「東京駅」で検索すると「緯度:35.681382 経度:139.766084」と出るので,
Longitude(経度)とLatitude(緯度)にそれぞれの値を入力すればよい。
なお,本来であればanimateTo()だけでマップの中心位置も移動してくれるはずなのだが,エミュレータ上で動作させた時にanimateTo()だけだと「現在地点を表すマーク」だけが移動し,マップの中心地点が移動してくれない,というケースが度々あった。
そのため,直後にsetCenter()して,マップの移動に関して念を押している。
また,GeoPointのgetLatitudeE6()などのメソッドが,実際に欲しい値よりも100万倍されたintの値で返ってくる点も盲点だ。
気付かないと「java.lang.IllegalArgumentException: latitude == 3.5681381E7」みたいな例外になる。
Convert GeoPoint to Location
http://stackoverflow.com/questions/34...
- geoPoint.getLatitudeE6()/1000000
さらに,アクティビティとは別スレッドからUI操作しようとすると,
「android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.」
みたいな例外になる。
これについては,別スレッドから直接UIを操作するのではなく,Handlerを経由する必要がある。
ProgressDialogの使い方メモ
http://magpad.jugem.jp/?eid=109
- Runnableから直接UIを書き換えようとすると、CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. で落ちる。(=このRunnableはUIスレッドとは別スレッドで立ち上がっているため)
GPSで現在地を表示するサンプルについて参考:
Android 自分の位置をMap上に描画
http://3a3k.blogspot.com/2009/12/android-map.html
- MyLocationOverlayクラスを利用
マーカーで特定の位置を指定・表示するには/GPSロケーションサービスと連携して現在位置を表示
http://www.atmarkit.co.jp/fsmart/arti...
- Android Mapsには、「MyLocationOverlay」というOverlayのサブクラスが用意されており、これを使用することで現在位置を簡単に表示
MyLocationOverlayでMapを作る
http://labs.techfirm.co.jp/android/m_...
- MyLocationOverlayクラスは、SensorListenerとLocationListenerを実装しているMapのOverlayクラス。このクラス一つで、方向、緯度、経度が取得できます
Google Mapで現在地を取得し表示する
http://techbooster.jpn.org/andriod/de...
- locationManager.getLastKnownLocation("gps") で最終取得地点を参照する方法
- 現在値の値は必ず取得できるわけではありません。よって、取得に失敗した場合の対応が必要
コードからズームコントロール
http://www.android-group.jp/index.php...
- MapController#setZoom()
Geocoderについて:
Google Maps API 逆ジオコーディング
http://code.google.com/intl/ja/apis/m...
- ジオコーディング:住所文字列から,緯度経度座標への変換
- 逆ジオコーディング:緯度経度座標から,住所文字列への変換
GPSの緯度経度から住所へ変換
http://d.hatena.ne.jp/oldfish/2008121...
- geocoder.getFromLocationを利用
地図描画と方位センサーの利用〜速習! Androidアプリケーション開発(6)〜
http://codezine.jp/article/detail/487...
- 住所から緯度/経度への変換を行う機能を提供してくれるのがGeocorder
バージョンによるGeocorderの利用制限について:
AndroidのGeocoderは 2.2のエミュレータで動かない(2011年5月の情報)
http://blog.goo.ne.jp/kokimurasaki/e/...
- Google Maps API for Androidのリファレンスに、Geocoderはありません。Androidの標準ライブラリだから
- android.location.Geocoderで探すと、Android2.2のエミュレータではダメで、2.1なら動く
- 2.2以上は未対応らしい。2.1で、住所の文字列から地図上の住所の候補、緯度経度を取得できた
Android 開発本を攻略しよう:第4章
http://tercel-sakuragaoka.blogspot.co...
- タップした場所の地名が画面上に表示されるように拡張されるはずだったが・・・
- 2010年8月の時点では、Androidエミュレータ上でGeocoderの機能は利用できませんでしたので、プログラムを試す際には実機で試してください
なお,自分の現在地を取得する方法は,上記のように
- MyLocationOverlayを使って
- runOnFirstFixで位置変更を監視して
- getMyLocationでGeoPointを取得
とするのが一番簡単。
もしこれを,その辺に転がっているサンプル通りに
- LocationManagerを使って
- requestLocationUpdatesで位置変更を監視して
- onLocationChanged内でLocationを取得
なんてやると,泥沼にはまるので注意。
Android の位置情報を確実に取る方法
http://d.hatena.ne.jp/glass-_-onion/2...
- LocationManager#requestLocationUpdates メソッドを呼んでも30分以上メソッドが呼び出されないなんて事も]
- LocationManager#removeUpdates メソッドを呼ばずにアクティビティを終了すると挙動がおかしくなる。requestLocationUpdates したままバックボタンを押してアクティビティを終了させると次回起動時に onLocationChanged メソッドがしばらく呼び出されなくなる
Androidで位置情報取得のベストプラクティス
http://d.hatena.ne.jp/orangesignal/20...
- GPS プロバイダを使用すると待てど暮らせど LocationListener#onLocationChanged が呼ばれないと記載のあった blog を見ましたが、一応数時間待てば呼ばれるケースもある
なお,runOnFirstFixに渡したRunnableタスクは,初回に位置を検出した1回目しか走ってくれない。
もしGPS情報の変更を検知するたびに,毎回何かの処理を行なわせたい場合は,
MyLocationOverlayをextendした独自クラスを作り,
その中でonLocationChange()をOverrideして,何かの処理を行なわせればよい。
そしてonLocationChange()の中で再度,runOnFirstFixにRunnableを渡せば,
現在地が移動するたびに毎度同じ処理を実行させることも可能になる。
補足1:今後必要になる情報
アプリの作りこみ:
Google MapでZoom event/Scroll event取得 - MapViewの拡張
http://d.hatena.ne.jp/sei10sa10/20110...
- MapViewを拡張しZoom level変更event、Center position変更eventを取得(Listenerに通知も)
Web版(JavaScript)のGoogle Maps APIも参考に
Google Maps APIを使う(後編)
http://ichitcltk.hustle.ne.jp/gudon/m...
- マーカやレイヤなどの機能の利用方法
Google Maps API V3 と Gears Geolocation API 使って Android のブラウザで現在位置情報を取得する
http://creco.net/2009/06/11/google_ma...
- Androidのブラウザ「Chrome Lite」が搭載しているGoogle GearsのGeolocation APIを利用
補足2:Graphical Editor上でMapViewを表示
もしレイアウトXMLのGraphical Editor上で,
MapViewのサイズ(width, height)をうまく調節できない場合,
下記のように一段LinearLayoutをはさんでやればうまく大きさを調節できる。
<LinearLayout android:layout_width="fill_parent" android:id="@+id/linearLayout1" android:gravity="center_vertical|center_horizontal" android:layout_height="345dp"> <!-- マップ --> <com.google.android.maps.MapView android:id="@+id/map_view1" android:clickable="true" android:enabled="true" android:layout_width="fill_parent" android:layout_height="300dp" android:apiKey="〜〜" ></com.google.android.maps.MapView> </LinearLayout>
補足3:マーケット公開用のアプリでMaps APIを利用したい場合
エミュレータ上で動かすためのデバッグ用のAPI Keyは,マーケット公開時には利用できないので注意。
マーケットに公開するアプリを作りたい場合,まず下記の手順に従って,マーケット公開用の署名を作成する。
パッケージエクスプローラ上で,
プロジェクトで右クリック
→Android Tools
→Export Signed Application Package
→Create new Keystoreを選択。
本番用Keystoreのファイルパスを指定する。
このファイルは複数PJ間で使いまわせる。
また,キーストアのパスワードも設定する。
続くダイアログ上で,
- エイリアス:適当な名前
- パスワード:上と同一のもの
- Validity:30(年)
- 組織:会社名
を入力。
Destination APK Fileの生成先を指定。
これで,マーケット公開用のapkファイルもできたし,Keystoreもできた。
そのKeystoreを使って,本稿で述べた手順に従ってフィンガープリントを再度作成し,GoogleのWebサイトからAPI Keyを再発行してもらう。
そのAPI Keyをアプリに埋め込めば,マーケット公開済みのアプリからもGoogle Mapを利用できる。
参考:
公開したAndroidアプリでMapViewが表示されないときの対処方法
http://android.roof-balcony.com/view/...
- エミュレータでは正常にGoogle MapViewが表示されていたのに、公開したAndroidアプリをMarketからダウンロードして起動すると、MapViewが表示されない!?
- 「Androidアプリへ署名の付加」の手順で作成したkeystoreに含まれているフィンガープリントを使って、APIキーを取得すればいい
Android入門【第7回:Androidマーケットで公開してみた】
http://labs.techfirm.co.jp/android/wa...
Eclipseから実機へapkファイルを送ってデバッグする時には,デバッグ用のAPIキーが必要になる。
少々煩雑か。
利用するAPIキーを素早く入れ替えたい場合は,strings.xmlに下記のように定義して・・・
<string name="gmap_api_key">〜〜</string>
レイアウト側で文字列参照すればよい。
android:apiKey="@string/gmap_api_key"
関連エントリー:
Androidアプリで,Google Mapsの地図上にアイコン画像を配置し,そのTapイベントに反応するサンプルコード
http://language-and-engineering.hatenablog.jp/entry/20110907/p1
- オーバレイクラスの一歩進んだ使い方
Androidアプリで,レイアウト用XMLの名前をいちいち指定せずに,自動的に画面を描画させよう (Rails風のCoCなレンダリング)
http://language-and-engineering.hatenablog.jp/entry/20110910/p1
- MapActivityの多重継承を解決するための,うまいクラス設計