行動應用程式有一項特別的功能,就是「位置辨識」。 由於行動裝置使用者會隨身攜帶自己的裝置,如果您能在應用程式中加入位置辨識功能,就可以根據使用者的所在位置,提供更符合需求的體驗。
程式碼範例
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
。您選擇的權限會決定 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) 推出了處理權限的新模型,可簡化使用者安裝與升級應用程式的程序。如果您的應用程式指定 23 以上的 API 級別,就可以使用新的權限模型。
如果您的應用程式支援新的權限模型,且裝置的作業系統是 Android 6.0 (Marshmallow) 以上版本,使用者在安裝或升級應用程式時就不需要授予任何權限。應用程式必須在執行階段檢查是否有必要權限;如果沒有,則必須要求權限。系統會向使用者顯示要求權限的對話方塊。
為了確保使用者獲得最佳體驗,請在適當時機要求使用者授予權限。 如果應用程式的運作需要位置資訊,請務必在應用程式啟動時要求位置存取權。您可加入歡迎畫面或說明小精靈,向使用者解釋為什麼要授予權限。
如果應用程式僅有部分功能需要取得權限,建議在應用程式執行需要權限的動作時才要求位置存取權。
應用程式必須妥善處理使用者未授予權限的情況。舉例來說,如果特定功能需要權限,應用程式可以停用該項功能。如果應用程式必須取得權限才能運作,則可停用其所有功能,並告知使用者必須授予權限。
下列程式碼範例會在啟用「我的位置」圖層前使用 AndroidX 程式庫檢查權限,然後再從支援資料庫導入 ActivityCompat.OnRequestPermissionsResultCallback
,處理權限要求的結果:
// 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
}
}
// 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()
可啟用地圖上的「我的位置」圖層。
以下範例說明「我的位置」圖層的簡單用法:
// 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
}
}
// 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
如要在 Android 應用程式中新增位置辨識功能,建議優先考量採用 Google Play 服務 Location API 方法。這個方法包括以下功能:
- 判斷裝置位置。
- 監聽位置變化。
- 如果該設備正在移動,可確定交通方式。
- 建立和監控預先定義的地理區域,也就是地理圍欄。
Location API 可以讓您輕鬆打造具備位置辨識功能的節能應用程式。和 Maps SDK for Android 一樣,Location API 也是隨附在 Google Play 服務 SDK 中一起發布。如要進一步瞭解 Location API,請參閱 Android 訓練課程「為應用程式加入位置辨識功能」或「Location API 參考資料」。程式碼範例包含在 Google Play 服務 SDK 中。