GPS機能を利用した現在地の描画
続いて、GPSから位置情報を取得し、マップ上に現在地を描画します。現在地の描画には方向付きのイメージを利用し、端末の向いている方位まで表現できるようにします。
GPSの利用
まずはGPSを利用し、地図上の現在地にアイコンを描画するところまでを実装します。
はじめに、現在値を描画するOverlayを作成します。基本は先ほどの目的値の場合と同じですが、方位を表現するために回転角の設定および、指定された回転角だけ回転させた後に描画する処理が追加してあります。
/** * イメージを描画するOverlay */ public class CurrentLocationOverlay extends Overlay { // 描画するイメージ private Bitmap bmp; // 描画する座標 private GeoPoint geoPoint; // 回転角 private float orientation= 0.0f; /** * コンストラクタ * @param bmp 描画するイメージ */ public CurrentLocationOverlay( Bitmap bmp) { this.bmp = bmp; } public GeoPoint getGeoPoint() { return geoPoint; } public void setGeoPoint(GeoPoint geoPoint) { this.geoPoint = geoPoint; } public float getOrientation() { return orientation; } public void setOrientation(float orientation) { this.orientation = orientation; } @Override public void draw(Canvas canvas, MapView mapView, boolean shadow) { // shadow = true,falseで2回呼び出される if( !shadow){ // Mapの表示位置と座標から画面の描画位置を算出 Projection pro = mapView.getProjection(); Point p = pro.toPixels(geoPoint, null); // 描画 int centerX = p.x - bmp.getWidth() / 2;; int centerY = p.y - bmp.getHeight() / 2; // 回転 canvas.rotate(orientation, p.x, p.y); // 画像の描画 canvas.drawBitmap(bmp, centerX, centerY, null); } } }
GPSを利用する場合には、ハードウェアのアクセス権限を追加する必要があるため、AndroidManifest.xmlを開き下記の太字部分を追加します。
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="sample.bizcard" android:versionCode="1" android:versionName="1.0"> <!-- インターネットへのアクセス権限 --> <uses-permission android:name="android.permission.INTERNET" /> <!-- GPSなど精度の高い位置情報を取得するハードウェアへのアクセス権限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> : :中略 : </manifest>
続いて、位置情報の取得~描画までの流れを実装します。位置情報は管理クラス(android.location.LocationManager)にリスナ(android.location.LocationListener)を登録する事で、変更の通知を受け取ることができます。
まずは、位置情報変更時の処理から実装しましょう。MapActivityのインナークラスとして、LocationListenerを実装して位置情報を受け取ったタイミングで現在地にアイコンを描画する処理を実装します。
// 現在地表示Overlay protected CurrentLocationOverlay currentLocationOverlay = null; /** * 位置情報の変化の通知を受け取るリスナ */ private class LocationAdaptor implements LocationListener{ /** * 位置情報の変化通知 * @param location 変化後の位置情報 */ @Override public void onLocationChanged(Location location) { setCurrent(location); } /** * プロバイダが利用不可になった通知 * @param provider プロバイダ */ @Override public void onProviderDisabled(String provider) { } /** * プロバイダが利用可になった通知 * @param provider プロバイダ */ @Override public void onProviderEnabled(String provider) { } /** * プロバイダのステータス変更の通知 * @param provider プロバイダ * @param status ステータス * @param extras 付加情報 */ @Override public void onStatusChanged(String provider, int status, Bundle extras) { } } /** * 現在地の設定 * @param location */ protected void setCurrent( Location location){ if( location == null){ return; } // LocationからGeoPointへ変換 Double latitude = location.getLatitude() * 1E6; Double longitude = location.getLongitude() * 1E6; GeoPoint geoPoint = new GeoPoint(latitude.intValue(), longitude.intValue()); // 地図の中央に設定 if( currentLocationToggle.isChecked()){ controller.setCenter( geoPoint); } // 目標値の座標にイメージを描画 if( currentLocationOverlay == null){ // 初回はOverlayを作成 Bitmap bmp = BitmapFactory.decodeResource( getResources(), R.drawable.current_location); currentLocationOverlay = new CurrentLocationOverlay(bmp); currentLocationOverlay.setGeoPoint( geoPoint); // Overlayの追加 map.getOverlays().add( currentLocationOverlay); } else{ // 2回目以降はOverlayを再利用 currentLocationOverlay.setGeoPoint( geoPoint); // 無効にして再描画 map.invalidate(); } }
次に、位置情報管理クラスに対するリスナの登録・削除の制御をしますが、ここで一点注意すべきポイントがあります。
今まで利用していたリスナは、ユーザー操作によるイベントだったため特に意識はしませんでしたが、位置情報やセンサーは、アプリケーションが背面に隠れている間もリスナ登録されていればイベントが通知され、限られたリソースとバッテリーを無駄に消費してしまいます。
このような無駄な消費を防ぐために、背面に行ったタイミング(onPause())でリスナ登録を削除し、再び前面に来たタイミング(onResume())でリスナ登録を行うといったような制御を行う必要があります。
// 位置情報管理クラス protected LocationManager locationManager = null; protected LocationAdaptor locationAdaptor = null; @Override protected void onResume() { // 位置情報管理クラスの取得 locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); locationAdaptor = new LocationAdaptor(); // 最終地点を取得 Location location = locationManager.getLastKnownLocation( LocationManager.GPS_PROVIDER); setCurrent( location); // 位置情報のリスナとして設定(1000=通知間隔(ms)、1=通知する最低移動距離(メートル) locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, 1000, 1, locationAdaptor); super.onResume(); } @Override protected void onPause() { // 位置情報のリスナから削除する locationManager.removeUpdates( locationAdaptor); locationManager = null; locationAdaptor = null; super.onPause(); }
現在値情報の実装確認
さて、位置情報に関する実装は終わりましたが、エミュレータのため実際に位置情報を受信する事はできません。
このような場合に、[Emulator Control]からダミーの位置情報を送り、実装の確認を行う事ができます。Eclipseのメニューから[Window]-[Show View]-[Other]-[Android]-[Emulator Control]を選択すると下記のビューが開きます。
[Manual]タグを選択し、Longitude(経度)、Latitude(緯度)を入力し、[Send]を押すとエミュレータに位置情報の変更を送る事ができます。緯度、経度の取得は、下記のようなサイトがありますので、これらを利用して求めます。