位置情報

モバイルアプリに特有の機能として、位置認識があります。 モバイル ユーザーはデバイスをあらゆる場所に持参するため、アプリに位置認識を追加すると、より状況に適したエクスペリエンスをユーザーに提供できます。

コードサンプル

GitHub の ApiDemos リポジトリには、地図での位置情報の使用方法を示すサンプルが含まれています。

Java

Kotlin

位置情報の使用

Android デバイスで使用可能な位置情報には、デバイスの現在地や(複数のテクノロジーを組み合わせて特定されます)、移動のルートと手段のほか、デバイスが事前定義済みの境界線を越えて移動しているかどうか(ジオフェンス)などがあります。アプリのニーズに応じて、いくつかの方法で位置情報を使用することができます。

  • 現在地レイヤを使用すると、デバイスの位置を地図上に簡単に表示できます。この方法では、位置情報は提供されません。
  • プログラムで位置情報をリクエストする場合は、Google Play 開発者サービスの Location API の使用が推奨されます。
  • LocationSource インターフェースを使用すると、カスタムの位置情報プロバイダを指定できます。

位置情報の利用許可

アプリでユーザーの位置情報を利用する必要がある場合は、適切な Android 位置情報の利用許可をアプリに追加して、許可をリクエストする必要があります。

Android には、ACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION という 2 つの位置情報の利用許可が用意されています。選択する利用許可によって API から返される位置情報の精度が異なります。必要な精度のレベルに応じて、Android 位置情報の利用許可を 1 つのみリクエストしてください。

  • android.permission.ACCESS_COARSE_LOCATION - デバイスの位置を特定するために、API に Wi-Fi またはモバイル デバイス(あるいはその両方)のモバイルデータを使用することを許可します。この場合、API は 1 街区とほぼ同じ精度で位置を返します。
  • android.permission.ACCESS_FINE_LOCATION - できる限り正確な位置を特定するために、API に使用可能な位置情報プロバイダ(グローバル ポジショニング システム(GPS)など)や、Wi-Fi またはモバイル デバイスのモバイルデータを使用することを許可します。

アプリ マニフェストに利用許可を追加する

次の利用許可のいずれかを、<manifest> 要素の子要素として Android マニフェストに追加します。精度の低い位置情報の利用許可は次のとおりです。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp" >
  ...
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
  ...
</manifest>

精度の高い位置情報の利用許可は次のとおりです。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp" >
  ...
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
  ...
</manifest>

実行時の権限をリクエストする

Android 6.0(Marshmallow)には、権限を処理するための新しいモデルが導入され、ユーザーがアプリのインストールやアップグレードを行う際のプロセスが合理化されています。アプリが API レベル 23 以降をターゲットとしている場合は、この新しい権限モデルを使用できます。

アプリで新しい権限モデルがサポートされていて、デバイスで Android 6.0(Marshmallow)以降が実行されている場合、アプリをインストールまたはアップグレードする際にユーザーがなんらかの権限を付与する必要はなく、アプリ自身が実行時に必要な権限が付与されているどうかを確認し、なければリクエストしなければなりません。ユーザーには、権限を要求するダイアログが表示されます。

最適なユーザー エクスペリエンスを提供するには、状況に適した権限をリクエストすることが重要です。 たとえば、アプリが機能するために位置情報が不可欠な場合は、アプリの起動時に位置情報の利用許可をリクエストする必要があります。それには、「ようこそ」の画面やウィザードで、この許可が必要な理由をユーザーに説明することをおすすめします。

アプリの一部の機能でのみ位置情報の利用許可が必要な場合は、アプリでその許可を必要とするアクションが行われるときにリクエストするようにします。

ユーザーが権限を付与しなかった場合は、アプリで適切に処理する必要があります。たとえば、その権限が必要な機能をアプリで無効にするなどです。アプリが機能するためにその権限が不可欠な場合は、アプリですべての機能を無効にして、権限の付与が必要であることをユーザーに知らせます。

次のコード サンプルでは、現在地レイヤを有効にする前にサポート ライブラリを使用して位置情報の利用許可を確認し、次に、サポート ライブラリから ActivityCompat.OnRequestPermissionsResultCallback を実装して、利用許可のリクエストの結果を処理しています。

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 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} is requested at run
 * time. If the permission has not been 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(GoogleMap googleMap) {
        map = googleMap;
        map.setOnMyLocationButtonClickListener(this);
        map.setOnMyLocationClickListener(this);
        enableMyLocation();
    }

    /**
     * Enables the My Location layer if the fine location permission has been granted.
     */
    private void enableMyLocation() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                == PackageManager.PERMISSION_GRANTED) {
            if (map != null) {
                map.setMyLocationEnabled(true);
            }
        } else {
            // Permission to access the location is missing. Show rationale and request permission
            PermissionUtils.requestPermission(this, LOCATION_PERMISSION_REQUEST_CODE,
                Manifest.permission.ACCESS_FINE_LOCATION, 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) {
            return;
        }

        if (PermissionUtils.isPermissionGranted(permissions, grantResults, Manifest.permission.ACCESS_FINE_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");
    }

}

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.OnRequestPermissionsResultCallback
import androidx.core.content.ContextCompat
import com.example.kotlindemos.PermissionUtils.PermissionDeniedDialog.Companion.newInstance
import com.example.kotlindemos.PermissionUtils.isPermissionGranted
import com.example.kotlindemos.PermissionUtils.requestPermission
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] is requested at run
 * time. If the permission has not been 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 ?: return
        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() {
        if (!::map.isInitialized) return
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
            == PackageManager.PERMISSION_GRANTED) {
            map.isMyLocationEnabled = true
        } else {
            // Permission to access the location is missing. Show rationale and request permission
            requestPermission(this, LOCATION_PERMISSION_REQUEST_CODE,
                Manifest.permission.ACCESS_FINE_LOCATION, true
            )
        }
    }

    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)) {
            // 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
    }
}

現在地レイヤ

現在地レイヤと現在地ボタンを使用して、地図にユーザーの現在地を表示できます。地図で現在地レイヤを有効にするには、mMap.setMyLocationEnabled() を呼び出します。

現在地レイヤの簡単な使用方法のサンプルを次に示します。

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;
    }
}

      

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
    }
}

      

現在地レイヤが有効になっている場合は、地図の右上に現在地ボタンが表示されます。デバイスの現在地がわかっている場合は、ユーザーがボタンをクリックすると、カメラで現在地が地図の中心に設定されます。現在地は、地図に小さな青い点で示されるか(デバイスが静止している場合)、山形として示されます(デバイスが移動している場合)。

次のスクリーンショットでは、地図の右上に現在地ボタンが、中心に現在地を示す青い点が表示されています。

現在地ボタンが表示されないようにするには、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 の一部として含まれています。