イベントリスナーの追加とMapFragmentのカスタマイズ

CodeZine / 2014年1月16日 14時0分

ActivityとFragmentのライフサイクル

 Android端末の通信機能と、国内外のさまざまなサイトで提供されているWeb APIを活用すれば、楽しいアプリをかんたんに作ることができます。この連載では、多種多様なWeb APIの紹介と、そのAPIを利用したサンプルアプリの制作を通じて、Androidアプリの実践的な開発を解説します。前回は、地図の中心位置の最寄り駅を表示するところまでを解説しました。第7回の本稿では、地図を移動した場合やマーカーの吹き出し(情報ウィンドウ)をタップした場合のイベント処理と、MapFragmentのカスタマイズを解説します。

■対象読者

 Androidアプリケーションの開発を始めたい方で、JavaとEclipseの基本的な知識がある方を対象とします。

■地図のイベント処理

 地図の移動やマーカーの吹き出しのタップを判断するには、そのイベントを監視する、イベントリスナーと呼ばれる処理を追加します。

●地図の位置を変更したときのイベントリスナー

 処理手順の1から4まではすでに解説しましたので、今回は、5と6の部分を解説します。

地図の中心位置を取得して、Web APIにアクセスする 結果を解析する マーカーを追加する マーカーと駅情報を保存する 地図の位置を変更したときのイベントリスナーを追加するマーカー(吹き出し)をタップしたときのイベントリスナーを追加する 次のようにActivityには、前回までのコードの修正と追加部分があります。

MainActivity.javaの一部
public class MainActivity extends Activity implements LoaderCallbacks<String> { ~中略~ // 地図の中心位置(1) private CameraPosition centerPosition = null; @Override protected void onCreate(Bundle savedInstanceState) { ~中略~ if (savedInstanceState == null) { ~中略~ } // 地図の中心位置を取得する(2) centerPosition = googleMap.getCameraPosition(); // APIのURLを準備してローダーを初期化する(3) execMoyori(centerPosition); // 地図を移動したときのリスナーを追加する(4) googleMap.setOnCameraChangeListener( // 無名クラス(5) new OnCameraChangeListener() { @Override public void onCameraChange(CameraPosition cameraPosition) { } } ); // 吹き出しのクリックリスナーを追加する(6) googleMap.setOnInfoWindowClickListener(new OnInfoWindowClickListener() { @Override public void onInfoWindowClick(Marker marker) { // 駅情報の取り出し(7) EkiInfo e = ekiMarkerMap.get(marker); Toast ts = Toast.makeText(getBaseContext(), e.name + "(" + e.distance + "m) " + "前の駅:" + e.prev + " 次の駅:" + e.next + " " + e.line, Toast.LENGTH_LONG); ts.setGravity(Gravity.TOP, 0, 200); ts.show(); } }); } // APIのURLを準備してローダーを初期化する(前回からパラメータを変更) public void execMoyori(CameraPosition centerPosition) { Bundle bundle = new Bundle(); // 緯度 bundle.putString("y", Double.toString(centerPosition.target.latitude)); // 経度 bundle.putString("x", Double.toString(centerPosition.target.longitude)); bundle.putString("moyori", "http://express.heartrails.com/api/json?method=getStations&"); // LoaderManagerの初期化 getLoaderManager().restartLoader(0, bundle, this); } ~中略~ }
 まず、前回のコードからの変更点を説明しておきましょう。

 MainActivityクラスに、地図の中心位置を保存するフィールドを追加しています(1)。また、地図の中心位置を取得する処理(2)を、execMoyoriメソッドから分離させました。これは、execMoyoriメソッドの、Web APIのURLを準備してローダーを初期化する処理を、後述するイベントのコールバック処理のなかでも利用したいためです。

 次に、イベントリスナーの処理を説明します。

 地図の移動の追随は、地図の位置を変更した際に発生するイベントを利用します。このイベントの発生時に何らかの処理を行うには、setOnCameraChangeListenerメソッドを使ってイベントリスナーを登録します(4)。これで、地図の位置が変更された際に、指定したメソッドがコールバックされるようになります。

 setOnCameraChangeListenerメソッドの引数は、OnInfoWindowClickListenerインターフェースを実装したクラスです。このインターフェースには、地図の位置を変更した際にコールバックされるonCameraChangeというメソッドのみ定義されています。

 このようなイベントリスナーの場合、無名クラスを使って、OnInfoWindowClickListenerインターフェースを実装するようにすると、簡潔に記述できます(5)。

 吹き出しに対するイベントリスナーも、同様に追加します(6)。ここでは、OnInfoWindowClickListenerインターフェースを実装する無名クラスを利用しています。

 吹き出しをタップすると、onInfoWindowClickメソッドがコールバックされます。そのメソッドの引数のmarkerは、タップされたマーカーとなっていますので、HashMapのekiMarkerMapから、最寄り駅情報を取り出しています(7)。

●地図に追随してマーカーを表示する

 最後に、地図の位置を変更した際にコールバックされるonCameraChangeメソッド内の処理を追加します。

MainActivity.javaの一部
public class MainActivity extends Activity implements LoaderCallbacks<String> { ~中略~ @Override protected void onCreate(Bundle savedInstanceState) { ~中略~ googleMap.setOnInfoWindowClickListener(new OnInfoWindowClickListener() { @Override public void onCameraChange(CameraPosition cameraPosition) { // 前回から500メートルより大きく移動したら(1) if (0.5 < calcDistance(centerPosition, cameraPosition)) { execMoyori(cameraPosition); centerPosition = cameraPosition; } } }); ~中略~ } // 2点間の距離を求める(km)(2) private double calcDistance(CameraPosition a, CameraPosition b) { double lata = Math.toRadians(a.target.latitude); double lnga = Math.toRadians(a.target.longitude); double latb = Math.toRadians(b.target.latitude); double lngb = Math.toRadians(b.target.longitude); double r = 6378.137; // 赤道半径 return r * Math.acos(Math.sin(lata) * Math.sin(latb) + Math.cos(lata) * Math.cos(latb) * Math.cos(lngb - lnga)); } ~後略~
 onCameraChangeメソッドでは、地図の中心位置が、前回保存した位置から500メールより大きくずれた場合に、Web APIを呼び出すようにしています。

 onCameraChangeメソッドの引数には、移動した最後の位置が格納されていますので、その位置と、保存しておいた位置(centerPosition)との2点間を比較しています。

 2点の緯度、経度から、距離を求めるには、地球の球体を考慮した計算が必要です(2)。計算式については、「2地点間の距離と方位角」というサイトを参考にしました。

 これで、地図を移動した場合に、追随してマーカーが表示されるようになります。



CodeZine

トピックスRSS

ランキング