モバイルアプリ特有の機能の 1 つとして、位置認識があります。 モバイル ユーザーは、どこに行くときでも必ずデバイスを携帯します。アプリに位置認識を追加することで、コンテキストに応じたユーザー エクスペリエンスを提供できるようになります。
コードサンプル
GitHub の ApiDemos リポジトリには、地図での位置情報の使用方法を示すサンプルが含まれています。
Kotlin
- MyLocationDemoActivity: 現在地レイヤ(実行時の権限を含む)を使用する
- LocationSourceDemoActivity: カスタム
LocationSource
を使用する - CurrentPlaceDetailsOnMap: Android デバイスの現在地を特定して、その場所(お店やサービス、またはその他のスポット)の詳細情報を表示する。現在の場所を選択して地図上に詳細を表示する方法についてのチュートリアルをご覧ください。
Java
- MyLocationDemoActivity: 現在地レイヤ(実行時の権限を含む)を使用する
- LocationSourceDemoActivity: カスタム
LocationSource
を使用する - CurrentPlaceDetailsOnMap: Android デバイスの現在地を特定して、その場所(お店やサービス、またはその他のスポット)の詳細情報を表示する。現在の場所を選択して地図上に詳細を表示する方法についてのチュートリアルをご覧ください。
位置情報の使用
Android デバイスで使用可能な位置情報には、デバイスの現在地(複数のテクノロジーを組み合わせて特定)や、方向と移動手段のほか、事前定義済みの境界線(ジオフェンス)を越えたデバイス移動の有無などがあります。位置情報は、アプリのニーズに応じていくつかの方法で使用できます。
- 現在地レイヤを使用すると、デバイスの位置を地図上に簡単に表示できます。この方法では、位置情報は提供されません。
- 位置情報をプログラマティックにリクエストする場合は、Google Play 開発者サービスの Location API の使用が推奨されます。
LocationSource
インターフェースを使用すると、カスタムの位置情報プロバイダを指定できます。
位置情報の利用許可
アプリでユーザーの位置情報を利用する必要がある場合は、適切な Android 位置情報の利用許可をアプリに追加して、許可をリクエストする必要があります。
Android には、ACCESS_COARSE_LOCATION
と ACCESS_FINE_LOCATION
という 2 つの位置情報の利用許可が用意されています。選択する利用許可によって API から返される位置情報の精度が異なります。
android.permission.ACCESS_COARSE_LOCATION
- API にデバイスのおおよその位置情報を返すことを許可します。この権限は、位置情報の精度に関するドキュメントの「おおよそ」に記載されているように、位置情報サービスから取得されるデバイスの位置情報の推定値を提供します。android.permission.ACCESS_FINE_LOCATION
- できる限り正確な位置を特定するために、API に使用可能な位置情報プロバイダ(グローバル ポジショニング システム(GPS)など)や、Wi-Fi またはモバイル デバイスのモバイルデータを使用することを許可します。
アプリ マニフェストに権限を追加する
アプリが機能するためにおおよその位置情報のみ必要な場合は、アプリのマニフェスト ファイルに ACCESS_COARSE_LOCATION
権限を追加します。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp" > ... <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> ... </manifest>
正確な位置情報が必要な場合は、アプリのマニフェスト ファイルに ACCESS_COARSE_LOCATION
権限と ACCESS_FINE_LOCATION
権限の両方を追加します。
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp" > ... <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> ... </manifest>
実行時の権限をリクエストする
Android 6.0(Marshmallow)では、権限を処理するための新しいモデルが導入され、ユーザーがアプリのインストールやアップグレードを行う際のプロセスが合理化されています。アプリが API レベル 23 以降をターゲットとしている場合は、この新しい権限モデルを使用できます。
アプリで新しい権限モデルがサポートされていて、デバイスで Android 6.0(Marshmallow)以降が実行されている場合、アプリをインストールまたはアップグレードする際にユーザーがなんらかの権限を付与する必要はありません。ただし実行時に必要な権限が付与されているどうかをアプリ自体が確認し、権限がなければリクエストしなければなりません。ユーザーには、権限を要求するダイアログが表示されます。
最適なユーザー エクスペリエンスを提供するには、状況に適した権限をリクエストすることが重要です。 たとえば、アプリが機能するために位置情報が不可欠な場合は、アプリの起動時に位置情報の利用許可をリクエストする必要があります。それには、ウェルカム画面やウィザードで、この許可が必要な理由をユーザーに説明することをおすすめします。
アプリの一部の機能でのみ位置情報の利用許可が必要な場合は、アプリでその許可を必要とするアクションが行われるときにリクエストするようにします。
ユーザーが権限を付与しなかった場合は、アプリで適切に処理する必要があります。たとえば、その権限が必要な機能をアプリで無効にするなどです。アプリが機能するためにその権限が不可欠な場合は、アプリですべての機能を無効にして、権限の付与が必要であることをユーザーに知らせます。
次のコードサンプルでは、現在地レイヤを有効にする前に AndroidX ライブラリを使用して位置情報の利用許可を確認し、次にサポート ライブラリから ActivityCompat.OnRequestPermissionsResultCallback
を実装して、利用許可のリクエストの結果を処理しています。
Kotlin
// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.example.kotlindemos import android.Manifest import android.annotation.SuppressLint import android.content.pm.PackageManager import android.location.Location import android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback import androidx.core.content.ContextCompat import com.example.kotlindemos.PermissionUtils.PermissionDeniedDialog.Companion.newInstance import com.example.kotlindemos.PermissionUtils.isPermissionGranted import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener import com.google.android.gms.maps.GoogleMap.OnMyLocationClickListener import com.google.android.gms.maps.OnMapReadyCallback import com.google.android.gms.maps.SupportMapFragment /** * This demo shows how GMS Location can be used to check for changes to the users location. The * "My Location" button uses GMS Location to set the blue dot representing the users location. * Permission for [Manifest.permission.ACCESS_FINE_LOCATION] and [Manifest.permission.ACCESS_COARSE_LOCATION] * are requested at run time. If either permission is not granted, the Activity is finished with an error message. */ class MyLocationDemoActivity : AppCompatActivity(), OnMyLocationButtonClickListener, OnMyLocationClickListener, OnMapReadyCallback, OnRequestPermissionsResultCallback { /** * Flag indicating whether a requested permission has been denied after returning in * [.onRequestPermissionsResult]. */ private var permissionDenied = false private lateinit var map: GoogleMap override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.my_location_demo) val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment? mapFragment?.getMapAsync(this) } override fun onMapReady(googleMap: GoogleMap) { map = googleMap googleMap.setOnMyLocationButtonClickListener(this) googleMap.setOnMyLocationClickListener(this) enableMyLocation() } /** * Enables the My Location layer if the fine location permission has been granted. */ @SuppressLint("MissingPermission") private fun enableMyLocation() { // 1. Check if permissions are granted, if so, enable the my location layer if (ContextCompat.checkSelfPermission( this, Manifest.permission.ACCESS_FINE_LOCATION ) == PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission( this, Manifest.permission.ACCESS_COARSE_LOCATION ) == PackageManager.PERMISSION_GRANTED ) { map.isMyLocationEnabled = true return } // 2. If if a permission rationale dialog should be shown if (ActivityCompat.shouldShowRequestPermissionRationale( this, Manifest.permission.ACCESS_FINE_LOCATION ) || ActivityCompat.shouldShowRequestPermissionRationale( this, Manifest.permission.ACCESS_COARSE_LOCATION ) ) { PermissionUtils.RationaleDialog.newInstance( LOCATION_PERMISSION_REQUEST_CODE, true ).show(supportFragmentManager, "dialog") return } // 3. Otherwise, request permission ActivityCompat.requestPermissions( this, arrayOf( Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION ), LOCATION_PERMISSION_REQUEST_CODE ) } override fun onMyLocationButtonClick(): Boolean { Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT) .show() // Return false so that we don't consume the event and the default behavior still occurs // (the camera animates to the user's current position). return false } override fun onMyLocationClick(location: Location) { Toast.makeText(this, "Current location:\n$location", Toast.LENGTH_LONG) .show() } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<String>, grantResults: IntArray ) { if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) { super.onRequestPermissionsResult( requestCode, permissions, grantResults ) return } if (isPermissionGranted( permissions, grantResults, Manifest.permission.ACCESS_FINE_LOCATION ) || isPermissionGranted( permissions, grantResults, Manifest.permission.ACCESS_COARSE_LOCATION ) ) { // Enable the my location layer if the permission has been granted. enableMyLocation() } else { // Permission was denied. Display an error message // Display the missing permission error dialog when the fragments resume. permissionDenied = true } } override fun onResumeFragments() { super.onResumeFragments() if (permissionDenied) { // Permission was not granted, display error dialog. showMissingPermissionError() permissionDenied = false } } /** * Displays a dialog with error message explaining that the location permission is missing. */ private fun showMissingPermissionError() { newInstance(true).show(supportFragmentManager, "dialog") } companion object { /** * Request code for location permission request. * * @see .onRequestPermissionsResult */ private const val LOCATION_PERMISSION_REQUEST_CODE = 1 } }
Java
// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.example.mapdemo; import android.Manifest.permission; import android.annotation.SuppressLint; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener; import com.google.android.gms.maps.GoogleMap.OnMyLocationClickListener; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.SupportMapFragment; import android.Manifest; import android.content.pm.PackageManager; import android.location.Location; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import android.widget.Toast; /** * This demo shows how GMS Location can be used to check for changes to the users location. The "My * Location" button uses GMS Location to set the blue dot representing the users location. * Permission for {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and {@link * android.Manifest.permission#ACCESS_COARSE_LOCATION} are requested at run time. If either * permission is not granted, the Activity is finished with an error message. */ public class MyLocationDemoActivity extends AppCompatActivity implements OnMyLocationButtonClickListener, OnMyLocationClickListener, OnMapReadyCallback, ActivityCompat.OnRequestPermissionsResultCallback { /** * Request code for location permission request. * * @see #onRequestPermissionsResult(int, String[], int[]) */ private static final int LOCATION_PERMISSION_REQUEST_CODE = 1; /** * Flag indicating whether a requested permission has been denied after returning in {@link * #onRequestPermissionsResult(int, String[], int[])}. */ private boolean permissionDenied = false; private GoogleMap map; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.my_location_demo); SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); mapFragment.getMapAsync(this); } @Override public void onMapReady(@NonNull GoogleMap googleMap) { map = googleMap; map.setOnMyLocationButtonClickListener(this); map.setOnMyLocationClickListener(this); enableMyLocation(); } /** * Enables the My Location layer if the fine location permission has been granted. */ @SuppressLint("MissingPermission") private void enableMyLocation() { // 1. Check if permissions are granted, if so, enable the my location layer if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { map.setMyLocationEnabled(true); return; } // 2. Otherwise, request location permissions from the user. PermissionUtils.requestLocationPermissions(this, LOCATION_PERMISSION_REQUEST_CODE, true); } @Override public boolean onMyLocationButtonClick() { Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT).show(); // Return false so that we don't consume the event and the default behavior still occurs // (the camera animates to the user's current position). return false; } @Override public void onMyLocationClick(@NonNull Location location) { Toast.makeText(this, "Current location:\n" + location, Toast.LENGTH_LONG).show(); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); return; } if (PermissionUtils.isPermissionGranted(permissions, grantResults, Manifest.permission.ACCESS_FINE_LOCATION) || PermissionUtils .isPermissionGranted(permissions, grantResults, Manifest.permission.ACCESS_COARSE_LOCATION)) { // Enable the my location layer if the permission has been granted. enableMyLocation(); } else { // Permission was denied. Display an error message // Display the missing permission error dialog when the fragments resume. permissionDenied = true; } } @Override protected void onResumeFragments() { super.onResumeFragments(); if (permissionDenied) { // Permission was not granted, display error dialog. showMissingPermissionError(); permissionDenied = false; } } /** * Displays a dialog with error message explaining that the location permission is missing. */ private void showMissingPermissionError() { PermissionUtils.PermissionDeniedDialog .newInstance(true).show(getSupportFragmentManager(), "dialog"); } }
現在地レイヤ
現在地レイヤと現在地ボタンを使用して、地図にユーザーの現在地を表示できます。地図で現在地レイヤを有効にするには、mMap.setMyLocationEnabled()
を呼び出します。
現在地レイヤの簡単な使用方法のサンプルを次に示します。
Kotlin
// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.maps.example.kotlin import android.annotation.SuppressLint import android.location.Location import android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener import com.google.android.gms.maps.GoogleMap.OnMyLocationClickListener import com.google.android.gms.maps.OnMapReadyCallback import com.google.android.gms.maps.SupportMapFragment import com.google.maps.example.R internal class MyLocationLayerActivity : AppCompatActivity(), OnMyLocationButtonClickListener, OnMyLocationClickListener, OnMapReadyCallback { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_my_location) val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment mapFragment.getMapAsync(this) } @SuppressLint("MissingPermission") override fun onMapReady(map: GoogleMap) { // TODO: Before enabling the My Location layer, you must request // location permission from the user. This sample does not include // a request for location permission. map.isMyLocationEnabled = true map.setOnMyLocationButtonClickListener(this) map.setOnMyLocationClickListener(this) } override fun onMyLocationClick(location: Location) { Toast.makeText(this, "Current location:\n$location", Toast.LENGTH_LONG) .show() } override fun onMyLocationButtonClick(): Boolean { Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT) .show() // Return false so that we don't consume the event and the default behavior still occurs // (the camera animates to the user's current position). return false } }
Java
// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.maps.example; import android.annotation.SuppressLint; import android.location.Location; import android.os.Bundle; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.SupportMapFragment; class MyLocationLayerActivity extends AppCompatActivity implements GoogleMap.OnMyLocationButtonClickListener, GoogleMap.OnMyLocationClickListener, OnMapReadyCallback { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my_location); SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); mapFragment.getMapAsync(this); } @SuppressLint("MissingPermission") @Override public void onMapReady(GoogleMap map) { // TODO: Before enabling the My Location layer, you must request // location permission from the user. This sample does not include // a request for location permission. map.setMyLocationEnabled(true); map.setOnMyLocationButtonClickListener(this); map.setOnMyLocationClickListener(this); } @Override public void onMyLocationClick(@NonNull Location location) { Toast.makeText(this, "Current location:\n" + location, Toast.LENGTH_LONG) .show(); } @Override public boolean onMyLocationButtonClick() { Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT) .show(); // Return false so that we don't consume the event and the default behavior still occurs // (the camera animates to the user's current position). return false; } }
現在地レイヤが有効になっている場合は、地図の右上に現在地ボタンが表示されます。デバイスの現在地がわかっている場合は、ユーザーがボタンをクリックすると、カメラで現在地が地図の中心に設定されます。現在地は、地図に小さな青い点で示されるか(デバイスが静止している場合)、山形として示されます(デバイスが移動している場合)。
次のスクリーンショットでは、地図の右上に現在地ボタンが、中心に現在地を示す青い点が表示されています。
現在地ボタンを表示しないようにするには、UiSettings.setMyLocationButtonEnabled(false)
を呼び出します。
アプリは、以下のイベントに応答できます。
- ユーザーが現在地ボタンをクリックすると、アプリは
GoogleMap.OnMyLocationButtonClickListener
からonMyLocationButtonClick()
コールバックを受け取ります。 - ユーザーが現在地を示す青い点をクリックすると、アプリは
GoogleMap.OnMyLocationClickListener
からonMyLocationClick()
コールバックを受け取ります。
Google Play 開発者サービスの Location API
Google Play 開発者サービスの Location API は、Android アプリに位置認識を追加するための方法として推奨されています。この API には、以下を行うための機能が含まれています。
- デバイスの位置を特定する。
- 現在地の変化をリッスンする。
- デバイスが移動している場合は、移動手段を特定する。
- ジオフェンスと呼ばれる、事前定義済みの地理的領域を作成して監視する。
Location API を使用すると、電力効率が高く、位置認識機能を備えたアプリを簡単に作成できます。Maps SDK for Android と同様に、Location API は Google Play 開発者サービス SDK の一部として配布されています。Location API について詳しくは、Android トレーニング クラスの位置認識機能をアプリに追加する方法または Location API リファレンスをご覧ください。コードサンプルは、Google Play 開発者サービス SDK の一部として含まれています。