A Day In The Life

とあるプログラマの備忘録

Android で LocationManager と MapView を連携させる

AndroidGoogle Map を使ったアプリを作成しようとしたのですが、ライブラリに癖があって結構苦労しました。そこで LocationManager を使って MapView(Google Map) 上に位置情報を表示させる簡単なサンプルプログラムを作成してみました。
地図にピンを表示する画像
サンプルプログラムの機能は以下です。

  • 位置情報取得
  • 地図にピンを描画
  • ピンをタップすると取得した位置情報を表示

事前準備

まずプロジェクトのビルドターゲットを Google APIs ライブラリに変更します。そのあとで Google Map 用の API Key を用意します。
なお AndroidGoogle Map を使うための事前準備に関してこちらの記事がとても参考になりました。

レイアウト XML

Activity で直接 MapView のインスタンスを作成して画面に配置する方法もありますが今回はレイアウト XML を使って MapView を配置します。
画面構成は以下の通りです。

  • 位置情報取得ボタン
  • 地図(MapView)
  • 位置情報取得時間表示用テキスト
  • 緯度経度精度表示用テキスト
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  >
  <Button
    android:id="@+id/button_start"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content"
    android:text="位置情報取得"
    />
  <com.google.android.maps.MapView
    android:id="@+id/map"
    android:layout_width="fill_parent"
    android:layout_height="320dip"
    android:enabled="true"
    android:clickable="true"
    android:apiKey="xxxxxxxxxxxxxxxxxxxx"
    />
  <TextView
    android:id="@+id/text_location_title"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content"
    />
  <TextView
    android:id="@+id/text_location_snippet"
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content"
    />
</LinearLayout>

OverLay

地図上に画像を表示させるにはオーバレイという仕組みが必要になります。ItemizedOverlay クラスはこのオーバレイの機能を提供してくれるクラスです。この ItemizedOverlay クラスを拡張した独自オーバレイクラスを作成して、地図上に表示させるピン画像の管理をさせます。
なおピン画像はこちらの記事の画像を使用させていただきました。

public class PinItemizedOverlay extends ItemizedOverlay<OverlayItem> {
  private List<OverlayItem> items = new ArrayList<OverlayItem>();
  public PinItemizedOverlay(Drawable defaultMarker) {
    super(boundCenterBottom(defaultMarker));
  }
  @Override
  protected OverlayItem createItem(int i) {
    return items.get(i);
  }
  @Override
  public int size() {
    return items.size();
  }
  public void addPin(GeoPoint point, String title, String snippet) {
    items.add(new OverlayItem(point, title, snippet));
    populate();
  }
}

Activity

Activity クラスの実装です。MapView を使う時は MapActivity クラスを拡張します。位置情報取得ボタンを押すと位置情報を取りにいき、その位置にピン画像を表示させます。また地図上のピン画像をタップすると画面下に位置情報が表示されるようにします。

public class MainActivity extends MapActivity 
    implements View.OnClickListener, LocationListener {
  private LocationManager locationManager;
  private MapView map;
  private Button buttonStart;	
  @Override
  protected void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    setContentView(R.layout.main);
    buttonStart = (Button)findViewById(R.id.button_start);
    buttonStart.setOnClickListener(this);
    map = (MapView)findViewById(R.id.map);
    map.getController().setZoom(18);
  }
  @Override
  protected boolean isRouteDisplayed() {
    return false;
  }
  @Override
  protected void onStop() {
    super.onStop();
    if (locationManager != null) locationManager.removeUpdates(this);
  }
  @Override
  public void onClick(View view) {
    locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
    List<String> providers = locationManager.getProviders(true);
    for (String provider : providers) {
      locationManager.requestLocationUpdates(provider, 0, 0, this);
    }
    buttonStart.setEnabled(false);
  }
  @Override
  public void onLocationChanged(Location location) {
    addPin(location);
    locationManager.removeUpdates(MainActivity.this);
    buttonStart.setEnabled(true);
  }
  @Override
  public void onProviderDisabled(String provider) {}
  @Override
  public void onProviderEnabled(String provider) {}
  @Override
  public void onStatusChanged(String provider, int status, Bundle extras {}
  private void addPin(Location location) {
    GeoPoint point = new GeoPoint((int)(location.getLatitude() * 1E6), 
				  (int)(location.getLongitude() * 1E6));
    Drawable pin = getResources().getDrawable(R.drawable.pin);
    PinItemizedOverlay overlay = new PinItemizedOverlay(pin) {
      @Override
      protected boolean onTap(int index) {
        OverlayItem item = (OverlayItem)getItem(index);
        TextView title = (TextView)findViewById(R.id.text_location_title);
        title.setText(item.getTitle());
        TextView snippet = (TextView)findViewById(R.id.text_location_snippet);
        snippet.setText(item.getSnippet());
        return super.onTap(index);
      }
    };
    map.getOverlays().add(overlay);
    overlay.addPin(point,
                   "位置情報:" + new Date(location.getTime()).toLocaleString(),
                   "緯度:" + location.getLatitude() + "経度:" + location.getLongitude() + "\n精度:±" + location.getAccuracy() + "m");
    map.getController().setCenter(point);
    map.invalidate();
  }	
}

addPin メソッドで PinItemizedOverlay クラスの onTap メソッドをオーバライドしているところがポイントです。このようにすることで地図上のオーバレイ画像をタップしたときに画面の内容をいろいろといじることができます。

サンプルコード

今回使用したサンプルコードをこちらに置いておきます。