这个简单的 Google Map 应用程序基于此Google Map compose library中的示例应用程序的简化版本。此外,我在此示例应用程序中添加了以下功能:
哎上传不了图片这里可以查看图片
设置谷歌云项目
您需要做的第一件事是设置一个 Google 云项目以生成一个 API 密钥,该密钥允许您使用 Google Maps SDK API。
- 在console.cloud.google.com中设置新项目
- 在您的项目仪表板中,转到API 概述
- 在API & Services页面中,转到Library
- 搜索Maps SDK for Android并启用它
- 返回API & Services页面,转到Credentials
- 选择+ CREATE CREDENTIALS,然后选择API 密钥
- 现在生成 API 密钥。单击API 密钥 1进行编辑。您可以将 API 密钥名称重命名为您喜欢的任何名称。对于此示例应用程序,您无需对此 API 密钥设置任何限制。
- 为应用程序限制选择无
- 为API 限制选择不限制密钥
这些只是简短的说明。详细官方说明见下图:
请注意,我没有设置任何计费帐户或启用计费,它仍然有效。
获得 API 密钥后,就可以实现代码了。
1.添加依赖build.gradle
这些是使用 Google Map compose 库所需的库。
implementation 'com.google.maps.android:maps-compose:2.1.1'
implementation 'com.google.android.gms:play-services-maps:18.0.2'
implementation "androidx.compose.foundation:foundation:1.2.0-beta02"
2. 设置 Secrets Gradle 插件
Secrets Gradle Plugin基本上是一个库,可帮助您隐藏 API 密钥,而无需将其提交到版本控制系统。
它允许您在local.properties
文件(未检入版本控制)中定义变量(例如 API 密钥)并检索变量。例如,您可以检索AndroidManifest.xml
文件中的变量。
这些是添加 Secrets Gradle 插件的步骤。
在项目级别build.gradle
:
buildscript {
...
dependencies {
classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1"
}
}
在应用程序级别build.gradle
:
plugins {
...
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
3.在里面添加MAPS_API_KEYlocal.properties
在local.properties
文件中,复制您从上述设置 Google Cloud 项目步骤中获得的 API 密钥并将其粘贴到此处。
MAPS_API_KEY=Your API Key here
4.添加元数据AndroidManifest.xml
为了读取MAPS_API_KEY
您在 中定义的变量local.properties
,您需要在 中<meta-data>
添加AndroidManifext.xml
。
<meta-data>
在标签中添加这个<application>
标签。
<application
...
<meta-data android:name="com.google.android.geo.API_KEY"
android:value="${MAPS_API_KEY}" />
...
</application>
如果你没有在上面设置 Secrets Gradle 插件,你会得到这个错误:
Attribute meta-data#com.google.android.geo.API_KEY@value at AndroidManifest.xml:14:13-44 requires a placeholder substitution but no value for <MAPS_API_KEY> is provided.
5.添加互联网和位置权限
由于应用需要访问互联网和位置权限,我们将这些权限添加到AndroidManifest.xml
.
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
...
</manifest>
6. 实施GoogleMap()
和Marker()
GoogleMap()
并且Marker()
是库中的可组合函数,我们可以调用它们来显示地图和地图上的标记。
地图显示当前位置(如果可用),默认为悉尼。
@Composable
private fun MyGoogleMap(
currentLocation: Location,
cameraPositionState: CameraPositionState,
onGpsIconClick: () -> Unit) {
val mapUiSettings by remember {
mutableStateOf(
MapUiSettings(zoomControlsEnabled = false)
)
}
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState,
uiSettings = mapUiSettings,
){
Marker(
state = MarkerState(
position = LocationUtils.getPosition(currentLocation)),
title = "Current Position"
)
}
GpsIconButton(onIconClick = onGpsIconClick)
DebugOverlay(cameraPositionState)
}
默认情况下,缩放控件处于打开状态。要关闭它,您可以创建一个新的MapUiSettings
并将其传递给GoogleMap()
as 参数。
地图也有 GPS 图标。当您单击它时,它会将相机移动到当前位置。如果之前未授予这些请求,它还请求位置许可并启用设备位置设置。
DebugOverlay
只是一个覆盖屏幕来显示当前的相机状态和位置。
7.请求位置许可
要检查是否已授予位置权限,请使用ContextCompat.checkSelfPermission()
API。
fun isLocationPermissionGranted(context: Context) : Boolean {
return (ContextCompat.checkSelfPermission(
context,
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED)
}
rememberLauncherForActivityResult()
如果未授予位置权限,则使用with设置是否授予或拒绝权限的回调ActivityResultContracts.RequestPermission()
。
要请求使用位置权限,请调用ActivityResultLauncher.launch()
.
@Composable
fun LocationPermissionsDialog(
onPermissionGranted: () -> Unit,
onPermissionDenied: () -> Unit,
) {
val requestLocationPermissionLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()) { isGranted: Boolean ->
if (isGranted) {
onPermissionGranted()
} else {
onPermissionDenied()
}
}
SideEffect {
requestLocationPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
}
}
注意:最初我使用了伴奏的权限库。它只有在获得许可的情况下才有效。当权限被拒绝并且我想再次请求权限时,它无法正常工作。所以我决定
rememberLauncherForActivityResult
改用。
8.启用位置设置
当已授予位置权限时,您要确保已打开位置设置。如果它关闭,您想请求用户打开它。
与上面请求位置权限类似,您使用 rememberLauncherForActivityResult()
注册启用位置设置请求回调。
val enableLocationSettingLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartIntentSenderForResult()
) { activityResult ->
if (activityResult.resultCode == Activity.RESULT_OK)
onSuccess()
else {
onFailure()
}
}
要检查位置设置是否打开,请调用 SettingsClient.checkLocationSettings()
API,该 API 返回Task<LocationSettingsResponse>
允许您设置失败和成功回调的 API。
如果回调失败,则表示设备位置设置关闭。在这种情况下,您想请求用户启用它(如果它是可解决的 - 例外是ResolvableApiException
)。为此,您使用从异常中获得ActivityResultLauncher.launch()
的分辨率调用 API 。PendingIntent
val locationRequest = LocationRequest.create().apply {
priority = Priority.PRIORITY_HIGH_ACCURACY
}
val locationRequestBuilder = LocationSettingsRequest.Builder()
.addLocationRequest(locationRequest)
val locationSettingsResponseTask = LocationServices.getSettingsClient(context)
.checkLocationSettings(locationRequestBuilder.build())
locationSettingsResponseTask.addOnSuccessListener {
onSuccess()
}
locationSettingsResponseTask.addOnFailureListener { exception ->
if (exception is ResolvableApiException){
try {
val intentSenderRequest =
IntentSenderRequest.Builder(exception.resolution).build()
enableLocationSettingLauncher.launch(intentSenderRequest)
} catch (sendEx: IntentSender.SendIntentException) {
sendEx.printStackTrace()
}
} else {
onFailure()
}
}
参考
LocationSettingDialog()
源码中。
8. 获取最后一个已知位置
最后,您要获取最后一个已知位置,如果设备位置设置打开,这也是当前位置。
首先,您设置LocationCallback()
接收LocationResult
具有最后已知位置信息的位置。然后删除回调以节省电量。
要请求位置更新,您可以通过传入、和来调用FusedLocationProviderClient.requestLocationUpdates()
API 。LocationRequest``LocationCallback``Looper
@SuppressLint("MissingPermission")
fun requestLocationResultCallback(
fusedLocationProviderClient: FusedLocationProviderClient,
locationResultCallback: (LocationResult) -> Unit
) {
val locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
super.onLocationResult(locationResult)
locationResultCallback(locationResult)
fusedLocationProviderClient.removeLocationUpdates(this)
}
}
val locationRequest = LocationRequest.create().apply {
interval = 0
fastestInterval = 0
priority = Priority.PRIORITY_HIGH_ACCURACY
}
Looper.myLooper()?.let { looper ->
fusedLocationProviderClient.requestLocationUpdates(
locationRequest,
locationCallback,
looper
)
}
}
结论
该应用程序在启动期间请求位置权限并请求启用位置设置。当用户单击 GPS 图标时,它会再次请求(对于尚未被批准的请求)。单击 GPS 图标时,它还会将相机移回当前位置。
有关详细信息以及如果您想使用该应用程序,请参阅以下源代码。
源代码
GitHub 存储库:Demo_SimpleGoogleMap
链接:https://vtsen.hashnode.dev/simple-google-map-app-jetpack-compose
网友评论