この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
前回の記事
では、アプリに Google Map を表示する手順をご紹介しました。
今回は Google Maps Android API v2 を使ってどのようなこと(カスタマイズ)ができるか、公式のサンプルプロジェクトをベースに逆引きリファレンスとしてまとめてみました!
サンプルプロジェクトのインポート
まずはサンプルプロジェクトをインポートしましょう。SDK Manager より「Google Play services」をインストールしていることが前提で進めます。 「 File > Import... 」を選択し、「Existing Android Code Into Workspace」を選択します。
ルートディレクトリは以下を選択します。
${SDK_ROOT}/extras/google/google_play_services/samples/map
インポートできました。
実機でアプリを起動させるためには AndroidManifest.xml の application タグ内の meta-data に自分で取得した Google Maps API Key を設定します。Google Maps API Key の取得方法はこちらを参照してください。
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="your_api_key"/>
あとは実機で実行できるはずです!
Google Maps Android API v2 でできること
ここまででサンプルアプリが実行できました。実機で試すとわかりますが、いくつかのカスタマイズ例がリストで表示されていると思います。それぞれどんなことをしているか確認していきたいと思います。
- ピンを立てる - BasicMap
- 指定の緯度・経度に移動する - Camera
- イベントをハンドリングする - Events
- 渋滞情報や現在位置を表示する - Layers
- 現在位置を好きな緯度・経度に設定する - Location Source Demo
- 各種操作の有効・無効を設定する - UI Settings
- 画像をオーバーレイで表示する - Ground Overlays
- マーカーをカスタマイズする - Markers
- ポリゴンを描画する - Polygons
- 線を描画する - Polylines
- 好きな画像をタイル表示する - Tile Overlays
- レイアウトXMLでオプションを指定する - Options
- 複数のマップを表示する - Multiple Maps
- 表示位置を保持する - RetainMap
- Fragment を使わずに表示する - Raw Map View
- マップを動的に追加する - Programmatically Add Map
ピンを立てる - Basic Map
Basic Map はマップにピンを立てるだけの簡単なサンプルです。MarkerOptions を作り、 GoogleMap インスタンスに addMarker() で追加するだけでピンが立てられます。
// Fragmentの取得
FragmentManager manager = getSupportFragmentManager();
SupportMapFragment frag = (SupportMapFragment) manager.findFragmentById(R.id.map_frag);
// GoogleMapの取得
GoogleMap map = frag.getMap();
// ピンを立てる
LatLng position = new LatLng(0, 0);
MarkerOptions options = new MarkerOptions();
options.position(position);
options.title("タイトル");
map.addMarker(options);
指定の緯度・経度に移動する - Camera
指定した緯度・経度に遷移できるサンプルです。アニメーションの有効・無効も選択可能です。また、アニメーションの途中で停止することもできます。
// シドニーに行く
CameraPosition sydney = new CameraPosition.Builder()
.target(new LatLng(-33.87365, 151.20689)).zoom(15.5f)
.bearing(0).tilt(25).build();
map.animateCamera(CameraUpdateFactory.newCameraPosition(sydney));
イベントをハンドリングする - Events
マップ操作中のイベントを取得するサンプルです。
// リスナーをセット
map.setOnMapClickListener(new OnMapClickListener() {
@Override
public void onMapClick(LatLng point) {
String text = "latitude=" + point.latitude + ", longitude=" + point.longitude;
Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG).show();
}
});
map.setOnMapLongClickListener(new OnMapLongClickListener() {
@Override
public void onMapLongClick(LatLng point) {
String text = "latitude=" + point.latitude + ", longitude=" + point.longitude;
Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG).show();
}
});
map.setOnCameraChangeListener(new OnCameraChangeListener() {
@Override
public void onCameraChange(CameraPosition position) {
LatLng point = position.target;
String text = "latitude=" + point.latitude + ", longitude=" + point.longitude;
Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG).show();
}
});
渋滞情報や現在位置を表示する - Layers
表示に Traffic (渋滞情報の表示)、 My Location (現在位置) のレイヤーを追加します。また、表示タイプを HYBRID, ROADMAP, SATELLITE, TERRAIN のいずれかに変更できます。
// 航空写真に変更
map.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
// 渋滞状況を表示
map.setTrafficEnabled(true);
// 現在の位置情報を表示
map.setMyLocationEnabled(true);
現在位置を好きな緯度・経度に設定する - Location Source Demo
現在位置を好きな緯度・経度に設定するには GoogleMap#setLocationSource() を使用します。引数には LocationSource インターフェースを実装した自作クラスのインスタンスを渡します。 自作クラスでは以下の手順で緯度・経度を設定します。 LocationSource インターフェースの activate() メソッドで渡される OnLocationChangedListener の onLocationChanged() を呼ぶことでその LocationSource の緯度・経度を設定できます。
- activate() メソッドで渡される OnLocationChangedListener を取得する
- Location インスタンスを作成して緯度・経度を設定する
- Location インスタンスを OnLocationChangedListener#onLocationChanged() で渡す
例えば以下のように実装します。
private class MyLocationSource implements LocationSource {
@Override
public void activate(OnLocationChangedListener listener) {
// 好きな緯度・経度を設定した Location を作成
Location location = new Location("MyLocation");
location.setLatitude(0);
location.setLongitude(0);
location.setAccuracy(100); // 精度
// Location に設定
listener.onLocationChanged(location);
}
@Override
public void deactivate() {
}
}
そして実装したクラスのインスタンスを以下のようにマップに設定します。setMyLocationEnabled() を true に設定しないと表示されないので注意してください。
MyLocationSource source = new MyLocationSource();
mMap.setLocationSource(source);
mMap.setMyLocationEnabled(true);
各種操作の有効・無効を設定する - UI Settings
UIの表示・非表示、ジェスチャーの有効・無効を設定できます。設定は GoogleMap#getUiSettings() で取得できる UiSettings インスタンスから設定します。その他に GoogleMap#setMyLocationEnabled() で現在位置表示の有効・無効も設定できます。
// 現在位置表示の有効化
mMap.setMyLocationEnabled(true);
// 設定の取得
UiSettings settings = mMap.getUiSettings();
// コンパスの有効化
settings.setCompassEnabled(true);
// 現在位置に移動するボタンの有効化
settings.setMyLocationButtonEnabled(true);
// ズームイン・アウトボタンの有効化
settings.setZoomControlsEnabled(true);
// すべてのジェスチャーの有効化
settings.setAllGesturesEnabled(true);
// 回転ジェスチャーの有効化
settings.setRotateGesturesEnabled(true);
// スクロールジェスチャーの有効化
settings.setScrollGesturesEnabled(true);
// Tlitジェスチャー(立体表示)の有効化
settings.setTiltGesturesEnabled(true);
// ズームジェスチャー(ピンチイン・アウト)の有効化
settings.setZoomGesturesEnabled(true);
画像をオーバーレイで表示する - Ground Overlays
指定の場所に Bitmap をオーバーレイで表示します。指定する Bitmap は BitmapDescriptor というクラスのインスタンスを生成して使います。 BitmapDescriptor を生成する BitmapDescriptorFactory というクラスがあるので、これを使用してリソースから呼び出します。アルファ値を調整することも可能です。
// 貼り付ける場所まで移動しておく
LatLng location = new LatLng(40.714086, -74.228697);
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(location, 11));
// マップに貼り付ける BitmapDescriptor を生成
BitmapDescriptor descriptor = BitmapDescriptorFactory.fromResource(R.drawable.newark_nj_1922);
// 貼り付ける設定
GroundOverlayOptions options = new GroundOverlayOptions();
options.image(descriptor);
options.anchor(0, 1);
options.position(location, 8600f, 6500f);
// マップに貼り付け・アルファを設定
GroundOverlay overlay = mMap.addGroundOverlay(options);
overlay.setTransparency(0.5F);
マーカーをカスタマイズする - Markers
マップ上に立てたピンをタップすると情報が表示できますが、表示される情報をカスタマイズすることができます。※ 情報を表示する View 内のテキストカラーや背景表示のカスタマイズ方法は解説が長くなりそうなので別な記事でご紹介します。
// オプション設定
MarkerOptions options = new MarkerOptions();
// 緯度・経度
options.position(new LatLng(0, 0));
// タイトル・スニペット
options.title("Title");
options.snippet("Snippet");
// アイコン(マップ上に表示されるピン)
options.icon(BitmapDescriptorFactory.fromResource(R.drawable.arrow));
// マーカーを貼り付け
mMap.addMarker(options);
ポリゴンを描画する - Polygons
マップ上にポリゴンを描画します。描画する内容の設定には PolygonOptions クラスを使います。描画する緯度・経度は add() メソッドを使うか、まとめてセットできる addAll() メソッドを使います。また addHole() メソッドで描画する場所の中で抜き (間を空ける) こともできます。
private void setUpMap() {
// 設定
PolygonOptions options = new PolygonOptions();
// 描画する座標を設定
options.addAll(createRectangle(new LatLng(-20, 130), 5, 5));
// 抜き
options.addHole(createRectangle(new LatLng(-22, 128), 1, 1));
// 塗り
options.fillColor(0x44ff0000);
// 線
options.strokeColor(Color.RED);
// 線幅
options.strokeWidth(5);
// 描画
mMap.addPolygon(options);
}
private List<LatLng> createRectangle(LatLng center, double halfWidth, double halfHeight) {
return Arrays.asList(new LatLng(center.latitude - halfHeight, center.longitude - halfWidth),
new LatLng(center.latitude - halfHeight, center.longitude + halfWidth),
new LatLng(center.latitude + halfHeight, center.longitude + halfWidth),
new LatLng(center.latitude + halfHeight, center.longitude - halfWidth),
new LatLng(center.latitude - halfHeight, center.longitude - halfWidth));
}
線を描画する - Polylines
線を描画するには PolylinesOptions クラスを使います。 基本的には PolygonOptions と同じ感じです。 ある緯度・経度とある緯度・経度を結ぶ線を描画しますが、 geodesic() メソッドを使うと測地線 (2点間を結ぶ最短曲線) を描画することもできます。
// 設定
PolylineOptions options = new PolylineOptions();
options.add(new LatLng(35.689488, 139.691706)); // 東京
options.add(new LatLng(34.052234, -118.243685)); // ロサンゼルス
options.color(0xcc00ffff);
options.width(10);
options.geodesic(true); // 測地線で表示
mMap.addPolyline(options);
好きな画像をタイル表示する - Tile Overlays
// マップを非表示
mMap.setMapType(GoogleMap.MAP_TYPE_NONE);
// 全部 Droid くん
TileProvider provider = new UrlTileProvider(256, 256) {
@Override
public URL getTileUrl(int x, int y, int zoom) {
URL url = null;
try {
url = new URL("http://www.android.com/media/wallpaper/gif/android_logo.gif");
} catch (MalformedURLException e) {
throw new AssertionError(e);
}
return url;
}
};
mMap.addTileOverlay(new TileOverlayOptions().tileProvider(provider));
レイアウトXMLでオプションを指定する - Options
レイアウトXML上で表示位置や各ジェスチャーの有効・無効などを設定することができます。 fragment タグ内に map から始まるプロパティを指定することで、設定された状態でマップを表示することができます。
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
class="com.google.android.gms.maps.SupportMapFragment"
map:cameraBearing="112.5"
map:cameraTargetLat="-33.796923"
map:cameraTargetLng="150.922433"
map:cameraTilt="30"
map:cameraZoom="13"
map:mapType="normal"
map:uiCompass="false"
map:uiRotateGestures="true"
map:uiScrollGestures="false"
map:uiTiltGestures="true"
map:uiZoomControls="false"
map:uiZoomGestures="true"/>
複数のマップを表示する - Multiple Maps
レイアウトXMLに複数の Fragment を置いておくことで、複数のマップを表示することもできます。多すぎるとパフォーマンスが低下するので注意して使いましょう。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="@+id/map_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/map_container2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:orientation="horizontal">
<fragment
android:id="@+id/map1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.5"
class="com.google.android.gms.maps.SupportMapFragment"
map:cameraTargetLat="40.72"
map:cameraTargetLng="-74.00"
map:cameraZoom="8"/>
<fragment
android:id="@+id/map2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.5"
class="com.google.android.gms.maps.SupportMapFragment"
map:cameraTargetLat="51.51"
map:cameraTargetLng="-0.12"
map:cameraZoom="8"/>
</LinearLayout>
<LinearLayout
android:id="@+id/map_container2"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:orientation="horizontal">
<fragment
android:id="@+id/map3"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.5"
class="com.google.android.gms.maps.SupportMapFragment"
map:cameraTargetLat="48.85"
map:cameraTargetLng="2.35"
map:cameraZoom="8"/>
<fragment
android:id="@+id/map4"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.5"
class="com.google.android.gms.maps.SupportMapFragment"
map:cameraTargetLat="35.69"
map:cameraTargetLng="139.69"
map:cameraZoom="8"/>
</LinearLayout>
</LinearLayout>
表示位置を保持する - Retain Map
MapFragment#setRetainInstance() を true に設定しておくと、 Activity の再生成時 ( onCreate() で savedInstanceState が渡される状態 ) に MapFragment#getMap() で以前表示していた状態でマップを復元できます。例えば onCreate() で以下のように GoogleMap インスタンスを取得します。
if (savedInstanceState == null) {
// Activity が初めて生成されたとき
mapFragment.setRetainInstance(true);
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map)).getMap();
} else {
// Activity が再生成されたとき
mMap = mapFragment.getMap();
}
Fragment を使わずに表示する - Raw MapView
MapFragment を介さず直接 MapView をレイアウトに配置して表示することもできます。 GoogleMap インスタンスは MapView#getMap() で取得することができます。それだけで表示は可能ですが、 MapView には Activity と同じようなライフサイクルメソッドがあるので、 Activity のライフサイクルメソッドと同じタイミングで呼ぶようにしましょう。
レイアウトXML
<com.google.android.gms.maps.MapView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Javaソース
// MapView の取得
MapView view = (MapView) findViewById(R.id.map);
// GoogleMap の取得
GoogleMap map = view.getMap();
// 以下は Activity のライフサイクルメソッドにあわせる
view.onResume();
view.onPause();
view.onDestroy();
view.onLowMemory();
view.onSaveInstanceState(outState);
【map15.png】
マップを動的に追加する - Programmatically Add Map
Javaソース上からマップを動的に追加するには、 MapFragment をインスタンス化し、 FragmentManager で対象の Fragment を指定、 FragmentTransaction で追加します。 APIレベル4から使用したい場合は MapFragment ではなく SupportMapFragment を使い、 FragmentManager と FragmentTransaction は SupportPackage のクラスを使用しましょう。
// FragmentManager の取得
FragmentManager manager = getSupportFragmentManager();
// SupportMapFragment の取得 (既にあれば)
SupportMapFragment frag = (SupportMapFragment) manager.findFragmentByTag("map");
if (frag == null) {
// SupportMapFragment の生成
frag = SupportMapFragment.newInstance();
// Fragment の追加
FragmentTransaction transaction = manager.beginTransaction();
transaction.add(android.R.id.content, frag, "map");
transaction.commit();
}
まとめ
個人的な感想としては、緯度・経度をレイアウトで指定できたり、マップ上に図をレンダリングできたり、ジェスチャーの有効・無効が細かく指定できたり、アプリに合わせて自由に、そして詳細にカスタマイズできそうなのでとても良い印象でした。とても使いやすいです! v2 がリリースされたついでにアプリ作ってみようかな〜ってかたは、サクッと導入できるしサクッとカスタマイズできるので、ぜひ参考にしながら使ってみてください!