sos の 作業メモ

プログラミングや英会話学習、マイルや旅行、日常生活など。最近はWebFormなASP.NETのお守りがお仕事です。

日々の生活にhappyをプラスする|ハピタス Gポイント

Google Maps Android API v2で現在位置をフォロー(deprecatedになりました)

2013/08/14追記

201308版GoogleMapsが公開されたので、そちらに対応したものを書いてみました。

201308版 Google Maps Android API v2で現在位置をフォロー - sos の 作業メモ

OnMyLocationChangeListenerはdeprecatedになりましたので、今後は使わないようにしないといけません。



もういっちょGoogleMapsネタ

my-location layer

v2には my-location layerというのがあります。GoogleMap#setMyLocationEnabledをtrueにすると、現在位置を地図上にプロットするだけでなく、リスナーに位置更新の通知を行ってくれる便利なやつです。これを使えば、常に自分の位置を地図の中心になるようにフォローするのは簡単です。

OnMyLocationChangeListenerで中心を移動

my-location layerは新しい位置をリスナーに通知してくれます。onMyLocationChangeメソッドの中で GoogleMap#animateCamera(...)を呼ぶだけでフォロー動作はバッチリです。

フォロー解除は?

iOSのMapKitにも似たような機能があるのですが、あちらはフォロー中にユーザーが地図をスクロールさせたりするとフォローが解除され、それ以降 現在位置のマーカーは動きますが、地図の中心は動かないという便利な機能が最初から入っています。このあたりはさすがApple

残念ながら、 Google Mapsにはそういう機能は用意されていません。 カメラの設定さえもイベントを捕まえて自分でやる野暮ったさなんですから当然ですね。

フォローをフラグ管理

仕方がないので、フォローの有無をフラグにして、フラグがたっているときだけフォロー動作を行うようにし、ユーザーが地図を動かしたらフラグをおろすような処理を組込みます。

野暮ったさその2なのですが、GoogleMapsではユーザーが地図をスクロールさせた時だけの検出はできません。スクロールさせるとOnCameraChangeListenerにカメラの位置の更新イベントがやってきますが、これはプログラム内でanimateCameraしたときや、zoomした時でさえもやってきます。

上に透明なViewをかぶせてタッチイベントを横取りとかは嫌

なんだかもうとてもメンドクサイ処理が頭をよぎりますが、そんなことはしたくないので、APIドキュメントを眺めつつ あれこれ悩んでみました。

getMyLocationと比べてみよう

自分でカメラ位置を変更した時でさえもonCameraChangeがやってきますが、この時にやってくるGameraPositionのtargetと、GoogleMap#getMyLocationで取得した位置は、非常に近くなります。それに対して、自分で動かしたときは両者の値はなかなか一致しません。

ということで、この二つを比較して、緯度、経度各々の差の絶対値が5x10^-6以下ならフォロー、そうでないならフォロー解除とすることにしました。

ユーザーが地図を動かしたときに、たまたま現在位置がセンターに来て、知らない間にフォローに入るということもありえますが…自分で試して一致させるのはほぼ不可能だったので、まぁこれでいいことにしておきます。

実装も完了したし、朝飯がてらちょっとテストドライブしてきます。

おまけ(初期位置とか)

CameraPositionを永続化する でも書きましたが、onCreateViewで初期座標とか設定してあげると良さそう。

復帰すべき初期の位置が不明な場合は、

CameraPosition.Builder builder = new  CameraPosition.Builder().bearing(bearing).tilt(tilt).zoom(zoom);
LocationManager mgr = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE); // 位置マネージャ取得
Location loc = mgr.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
if (loc == null) {
    loc = mgr.getLastKnownLocation(LocationManager.GPS_PROVIDER);
}
if (loc != null) {
    builder.target(new LatLng(loc.getLatitude(), loc.getLongitude()));
}
mMap.moveCamera(CameraUpdateFactory.newCameraPosition(builder.build()));

なんて処理を入れるとさらに良いかもしれませんね。PASSIVE_PROVIDERで現在位置に近い値がとれれば幸せになれます。