使用 Kotlin 进行高级 Android 开发 04.1:Android Google 地图

此 Codelab 是“使用 Kotlin 进行高级 Android 开发”课程的一部分。如果您按顺序学习这些 Codelab,您将会充分发掘课程的价值,但并不强制要求这样做。“使用 Kotlin 进行高级 Android 开发”Codelab 着陆页列出了所有课程 Codelab。

使用 Google 地图构建应用,您可以向应用添加各种功能,例如卫星图像、强大的地图界面控件、位置信息跟踪和地点标记。您可以展示自己的数据集内的信息(例如有名钓鱼区或攀爬区的位置信息),从而为标准 Google 地图增加价值。您还可以打造游戏,让玩家在寻宝或增强现实等游戏中探索现实世界。

在本课程中,您会创建一个名为 Wander 的 Google 地图应用,此应用用于显示自定义地图并展示用户的位置信息。

前提条件

掌握了以下各方面的知识:

  • 如何使用 Android Studio 创建和运行基本的 Android 应用?
  • 如何创建和管理资源(例如字符串)?
  • 如何使用 Android Studio 重构代码和重命名变量?
  • 如何以用户身份使用 Google 地图?
  • 如何设置运行时权限?

学习内容

  • 如何从 Google API 控制台获取 API 密钥并向应用注册此密钥
  • 如何将 Google 地图集成到您的应用中
  • 如何显示不同的地图类型
  • 如何设置 Google 地图样式
  • 如何向地图添加标记
  • 如何允许用户在地图注点 (POI) 上放置标记
  • 如何启用位置信息跟踪功能
  • 如何创建包含嵌入式 Google 地图的 Wander 应用
  • 如何为应用创建自定义功能,例如标记和样式
  • 如何在应用中启用位置信息跟踪功能

在此 Codelab 中,您会创建 Wander 应用,此应用显示包含自定义样式的 Google 地图。利用 Wander 应用,您可以将标记拖放到相应位置上、添加叠层,以及实时查看自己的位置。

Maps SDK for Android 需要 API 密钥。如需获取 API 密钥,请在 API 和服务页面中注册您的项目。该 API 密钥与将此应用关联到其作者的数字证书相关联。如需详细了解如何使用数字证书以及如何为应用签名,请参阅为应用签名

在此 Codelab 中,您将使用调试证书的 API 密钥。如对您的调试 build 进行签名中所述,调试证书并不安全。使用 Maps SDK for Android 的已发布 Android 应用需要另一个 API 密钥,即发布证书的密钥。如需详细了解如何获取发布证书,请参阅获取 API 密钥

Android Studio 包含一个 Google Maps Activity 模板,此模板可生成有用的模板代码。此模板代码包含一个 google_maps_api.xml 文件,其中包含一个可简化 API 密钥获取流程的链接。

第 1 步:使用地图模板创建 Wander 项目

  1. 新建一个 Android Studio 项目。
  2. 选择 Google Maps Activity 模板。

  1. 将项目命名为 Wander
  2. 将最低 API 级别设置为 API 19。确保语言为 Kotlin
  3. 点击 Finish
  4. 应用构建完成后,请查看您的项目以及 Android Studio 为您创建的以下地图相关文件:

google_maps_api.xml - 使用此配置文件保存 API 密钥。该模板会生成两个 google_maps_api.xml 文件:一个用于调试,一个用于发布。调试证书的 API 密钥文件位于 src/debug/res/values。发布证书的 API 密钥文件位于 src/release/res/values。在此 Codelab 中,您只使用调试证书。

activity_maps.xml - 此布局文件包含一个填满整个屏幕的 fragment。SupportMapFragment 类是 Fragment 类的一个子类。SupportMapFragment 是在应用中放置地图的最简单方法。它是地图视图的封装容器,可自动处理必要的生命周期需求。

您可以使用任何 ViewGroup 中的 <fragment> 标记在布局文件中添加 SupportMapFragment,并包含一个额外的 name 属性。

android:name="com.google.android.gms.maps.SupportMapFragment"

MapsActivity.java - MapsActivity.kt 文件会实例化 onCreate() 方法中的 SupportMapFragment,并使用该类的 getMapAsync() 自动初始化地图系统和视图。包含 SupportMapFragment 的 activity 必须实现 OnMapReadyCallback 接口以及该接口的 onMapReady() 方法。加载地图时,系统会调用 onMapReady() 方法。

第 2 步:获取 API 密钥

  1. 打开 google_maps_api.xml 文件的调试版本。
  2. 在此文件中,查找包含长网址的注释。此网址的参数包含应用的特定信息。
  3. 将此网址复制和粘贴到浏览器中。
  4. 根据提示在 APIs & Services网 页上创建项目。由于所提供网址中的参数,此网页知道要自动启用 Maps SDK for Android。
  5. 点击 Create an API Key
  6. 在下一页上,转到“API Keys”部分,然后点击您刚刚创建的密钥。
  7. 点击限制密钥,然后选择 Maps SDK for Android,以将密钥限制用于 Android 应用。
  8. 复制生成的 API 密钥。它以“AIza"”开头。
  9. google_maps_api.xml 文件中,将密钥粘贴到显示 YOUR_KEY_HEREgoogle_maps_key 字符串中。
  10. 运行应用。您应可以在 activity 中看到嵌入式地图,其中标记设置在澳大利亚悉尼。(悉尼标记是模板自带的,您稍后可以对其进行更改。)

第 3 步:重命名 mMap

MapsActivity 具有一个名为 mMap 的专用 lateinit var,其类型为 GoogleMap。为了遵循 Kotlin 命名惯例,请将 mMap 的名称更改为 map

  1. MapsActivity 中,右键点击 mMap,然后依次点击 Refactor > Rename…

  1. 将此变量名称更改为 map

请注意,onMapReady() 函数中对 mMap 的所有引用也会变为引用 map

Google 地图包含多种地图类型:标准、混合、卫星、地形和“无”(完全没有地图)。

标准地图

卫星地图

混合地图

地形地图

每种类型的地图都会提供不同类型的信息。例如,在汽车中使用地图进行导航时,查看街道名称很有帮助,因此您可以使用常规选项。徒步旅行时,地形地图可以帮您确定还要攀爬多长距离才能到达山顶。

在此任务中,您将执行以下操作:

  1. 添加一个应用栏,其中包含一个允许用户更改地图类型的选项菜单。
  2. 将地图的起始位置移至您的住宅位置。
  3. 添加对标记的支持,标记指示地图上的单个位置,并可包含标签。

添加地图类型的菜单

在此步骤中,您会添加一个包含选项菜单的应用栏,用户可在此菜单中更改地图类型。

  1. 如需创建新的菜单 XML 文件,请右键点击 res 目录,然后依次选择 New > Android Resource File
  2. 在对话框中,将该文件命名为 map_options
  3. 选择 Menu 作为资源类型。
  4. 点击 OK
  5. Code 标签页中,将新文件中的代码替换为以下代码,以创建地图菜单选项。“无”地图类型会被省略,因为“无”会使系统根本不包含任何地图。此步骤会导致错误,但您可以在下一步中解决此问题。
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
   <item
       android:id="@+id/normal_map"
       android:title="@string/normal_map"
       app:showAsAction="never" />
   <item
       android:id="@+id/hybrid_map"
       android:title="@string/hybrid_map"
       app:showAsAction="never" />
   <item
       android:id="@+id/satellite_map"
       android:title="@string/satellite_map"
       app:showAsAction="never" />
   <item
       android:id="@+id/terrain_map"
       android:title="@string/terrain_map"
       app:showAsAction="never" />
</menu>
  1. strings.xml 中,为 title 属性添加资源以解决错误。
<resources>
   ...
   <string name="normal_map">Normal Map</string>
   <string name="hybrid_map">Hybrid Map</string>
   <string name="satellite_map">Satellite Map</string>
   <string name="terrain_map">Terrain Map</string>
   <string name="lat_long_snippet">Lat: %1$.5f, Long: %2$.5f</string>
   <string name="dropped_pin">Dropped Pin</string>
   <string name="poi">poi</string>
</resources>
  1. MapsActivity 中,替换 onCreateOptionsMenu() 方法,并通过 map_options 资源文件膨胀菜单。
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
   val inflater = menuInflater
   inflater.inflate(R.menu.map_options, menu)
   return true
}
  1. MapsActivity.kt 中,替换 onOptionsItemSelected() 方法。使用地图类型常量更改地图类型,以反映用户的选择。
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
   // Change the map type based on the user's selection.
   R.id.normal_map -> {
       map.mapType = GoogleMap.MAP_TYPE_NORMAL
       true
   }
   R.id.hybrid_map -> {
       map.mapType = GoogleMap.MAP_TYPE_HYBRID
       true
   }
   R.id.satellite_map -> {
       map.mapType = GoogleMap.MAP_TYPE_SATELLITE
       true
   }
   R.id.terrain_map -> {
       map.mapType = GoogleMap.MAP_TYPE_TERRAIN
       true
   }
   else -> super.onOptionsItemSelected(item)
}
  1. 运行应用。
  2. 点击 更改地图类型。请注意地图外观在不同模式之间会如何变化。

默认情况下,onMapReady() 回调包含的代码会将标记放置在创建 Google 地图时所处的澳大利亚悉尼。默认回调还会以动画形式将地图平移到悉尼。

在此任务中,您会将地图的相机移至您的住宅,缩放到您指定的级别,并在地图上放置标记。

第 1 步:缩放至您的住宅并添加标记

  1. MapsActivity.kt 文件中,找到 onMapReady() 方法。移除其中用于在悉尼放置标记并移动相机的代码。现在,您的方法应如下所示。
override fun onMapReady(googleMap: GoogleMap) {
   map = googleMap

}
  1. 按照相关说明进行操作,查找您的住宅的纬度和经度。
  2. 为纬度和经度分别创建一个值,然后输入两者的浮点值。
val latitude = 37.422160
val longitude = -122.084270
  1. 创建一个名为 homeLatLng 的新 LatLng 对象。在 homeLatLng 对象中,传入您刚刚创建的值。
val homeLatLng = LatLng(latitude, longitude)
  1. 针对您要在地图上放大到的程度创建 val。使用 15f 缩放级别。
val zoomLevel = 15f

缩放级别可控制您在地图上放大到多大程度。通过以下列表,您可以了解在每个缩放级别看到的地图细节水平:

  • 1:全球
  • 5:大陆/洲
  • 10:城市
  • 15:街道
  • 20:建筑物
  1. 通过对 map 对象调用 moveCamera() 函数将相机移至 homeLatLng,然后使用 CameraUpdateFactory.newLatLngZoom() 传入 CameraUpdate 对象。传入 homeLatLng 对象和 zoomLevel
map.moveCamera(CameraUpdateFactory.newLatLngZoom(homeLatLng, zoomLevel))
  1. 向地图上的 homeLatLng 添加标记。
map.addMarker(MarkerOptions().position(homeLatLng))

最终的方法应如下所示:

override fun onMapReady(googleMap: GoogleMap) {
   map = googleMap

   //These coordinates represent the latitude and longitude of the Googleplex.
   val latitude = 37.422160
   val longitude = -122.084270
   val zoomLevel = 15f

   val homeLatLng = LatLng(latitude, longitude)
   map.moveCamera(CameraUpdateFactory.newLatLngZoom(homeLatLng, zoomLevel))
   map.addMarker(MarkerOptions().position(homeLatLng))
}
  1. 运行应用。地图应该会平移到您的住宅,缩放到所需级别,并在您的住宅上放置标记。

第 2 步:允许用户通过长按来添加标记

在此步骤中,您将在用户轻触并按住地图上的某个位置时添加一个标记。

  1. MapsActivity 中创建一个名为 setMapLongClick() 的方法桩,此方法桩把 GoogleMap 作为参数。
  2. 向此地图对象附加 setOnMapLongClickListener 监听器。
private fun setMapLongClick(map:GoogleMap) {
   map.setOnMapLongClickListener { }
}
  1. setOnMapLongClickListener() 中,调用 addMarker() 方法。传入一个新的 MarkerOptions 对象,并将位置设置为传入的 LatLng
private fun setMapLongClick(map: GoogleMap) {
   map.setOnMapLongClickListener { latLng ->
       map.addMarker(
           MarkerOptions()
               .position(latLng)
       )
   }
}
  1. onMapReady() 方法结束时,使用 map 调用 setMapLongClick()
override fun onMapReady(googleMap: GoogleMap) {
   ...

   setMapLongClick(map)
}
  1. 运行应用。
  2. 轻触并按住地图,在某个位置上放置标记。
  3. 点按此标记,让其在屏幕上居中显示。

第 3 步:为标记添加信息窗口

在此步骤中,您会添加 InfoWindow,用于在用户点按标记时显示标记坐标。

  1. setMapLongClick()setOnMapLongClickListener() 中,为 snippet 创建一个 val。代码段是显示在标题之后的附加文本。您的代码段会显示标记的纬度和经度。
private fun setMapLongClick(map: GoogleMap) {
   map.setOnMapLongClickListener { latLng ->
       // A snippet is additional text that's displayed after the title.
       val snippet = String.format(
           Locale.getDefault(),
           "Lat: %1$.5f, Long: %2$.5f",
           latLng.latitude,
           latLng.longitude
       )
       map.addMarker(
           MarkerOptions()
               .position(latLng)
       )
   }
}
  1. addMarker() 中,使用 R.string.dropped_pin 字符串资源将标记的 title 设置为“已放置图钉”。
  2. 将标记的 snippet 设为 snippet

完成后的函数如下所示:

private fun setMapLongClick(map: GoogleMap) {
   map.setOnMapLongClickListener { latLng ->
       // A Snippet is Additional text that's displayed below the title.
       val snippet = String.format(
           Locale.getDefault(),
           "Lat: %1$.5f, Long: %2$.5f",
           latLng.latitude,
           latLng.longitude
       )
       map.addMarker(
           MarkerOptions()
               .position(latLng)
               .title(getString(R.string.dropped_pin))
               .snippet(snippet)

       )
   }
}
  1. 运行应用。
  2. 轻触并按住地图以放置地点标记。
  3. 点按此标记可显示信息窗口。

第 4 步:添加 POI 监听器

默认情况下,地图注点 (POI) 将与对应的图标一起显示在地图上。POI 包括公园、学校和政府大楼,等等。当地图类型设为 normal 时,商家 POI 也会显示在地图上。商家 POI 表示商家,例如商店、餐馆和酒店。

在这一步中,您将向地图添加 GoogleMap.OnPoiClickListener。此点击监听器会在用户点击 POI 时立即在地图上放置标记。此点击监听器还会显示包含 POI 名称的信息窗口。

  1. MapsActivity 中创建一个名为 setPoiClick() 的方法桩,此方法桩把 GoogleMap 作为参数。
  2. setPoiClick() 方法中,对传入的 GoogleMap 设置 OnPoiClickListener
private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->

   }
}
  1. setOnPoiClickListener() 中,为标记创建一个 val poiMarker
  2. 使用 map.addMarker() 将其设置为标记,并通过 MarkerOptionstitle 设置为地图注点的名称。
private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->
       val poiMarker = map.addMarker(
           MarkerOptions()
               .position(poi.latLng)
               .title(poi.name)
       )
   }
}
  1. setOnPoiClickListener() 函数中,对 poiMarker 调用 showInfoWindow(),系统会立即显示信息窗口。
poiMarker.showInfoWindow()

setPoiClick() 函数的最终代码应如下所示。

private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->
       val poiMarker = map.addMarker(
           MarkerOptions()
               .position(poi.latLng)
               .title(poi.name)
       )
       poiMarker.showInfoWindow()
   }
}
  1. onMapReady() 结束时,调用 setPoiClick() 并传入 map
override fun onMapReady(googleMap: GoogleMap) {
   ...

   setPoiClick(map)
}
  1. 运行应用并找到 POI,例如公园或咖啡馆。
  2. 点按 POI,在其上放置标记,并在信息窗口中显示该 POI 的名称。

您可以通过多种方式自定义 Google 地图,让地图拥有独特的外观和风格。

您可以使用可用的 XML 属性自定义 MapFragment 对象,就像自定义任何其他 fragment 一样。但在此步骤中,您将使用 GoogleMap 对象上的方法自定义 MapFragment 的内容的外观和风格。

若要为您的地图创建自定义样式,您需要生成一个 JSON 文件,以指定地图中地图项的显示方式。您无需手动创建此 JSON 文件。Google 提供了 Maps Platform 样式设置向导,可在您直观地设置地图样式后生成 JSON。在此任务中,您将使用复古主题为地图设置样式,这意味着地图会使用复古颜色并添加彩色道路。

第 1 步:为地图创建样式

  1. 在浏览器中前往 https://mapstyle.withgoogle.com/
  2. 选择 Create a Style
  3. 选择 Retro

  1. 点击 More Options

  1. Feature type 列表中,依次选择 Road > Fill
  2. 将道路的颜色更改为您选择的任意颜色(例如粉色)。

  1. 点击 Finish

  1. 从出现的对话框中复制 JSON 代码,如果需要,将其存储在纯文本备注中,供下一步使用。

第 2 步:将样式添加到地图

  1. 在 Android Studio 的 res 目录中,创建资源目录并将其命名为 raw。您可以使用 JSON 代码等 raw 目录资源。
  2. res/raw 中创建名为 map_style.json 的文件。
  3. 将您保存的 JSON 代码粘贴到这个新的资源文件中。
  4. MapsActivity 中,在 onCreate() 方法上方创建一个 TAG 类变量。这用于日志记录目的。
private val TAG = MapsActivity::class.java.simpleName
  1. MapsActivity 中,创建一个 setMapStyle() 函数,该函数接受 GoogleMap
  2. setMapStyle() 中,添加 try{} 代码块。
  3. try{} 代码块中,为样式设置的成功状态创建 val success。(添加以下 catch 代码块。)
  4. try{} 代码块中,将 JSON 样式设置为地图,对 GoogleMap 对象调用 setMapStyle()。传入一个 MapStyleOptions 对象,以加载 JSON 文件。
  5. 将结果分配给 successsetMapStyle() 方法会返回一个布尔值,指示解析样式文件并设置样式的成功状态。
private fun setMapStyle(map: GoogleMap) {
   try {
       // Customize the styling of the base map using a JSON object defined
       // in a raw resource file.
       val success = map.setMapStyle(
           MapStyleOptions.loadRawResourceStyle(
               this,
               R.raw.map_style
           )
       )
   }
}
  1. 对结果为 false 的 success 添加 if 语句。如果样式设置失败,请输出一条日志,指明解析已失败。
private fun setMapStyle(map: GoogleMap) {
   try {
       ...
       if (!success) {
           Log.e(TAG, "Style parsing failed.")
       }
   }
}
  1. 添加一个 catch{} 代码块,以处理缺少样式文件的情况。在 catch 代码块中,如果无法加载文件,则会抛出 Resources.NotFoundException
private fun setMapStyle(map: GoogleMap) {
   try {
       ...
   } catch (e: Resources.NotFoundException) {
       Log.e(TAG, "Can't find style. Error: ", e)
   }
}

完成后的方法应类似于以下代码段:

private fun setMapStyle(map: GoogleMap) {
   try {
       // Customize the styling of the base map using a JSON object defined
       // in a raw resource file.
       val success = map.setMapStyle(
           MapStyleOptions.loadRawResourceStyle(
               this,
               R.raw.map_style
           )
       )

       if (!success) {
           Log.e(TAG, "Style parsing failed.")
       }
   } catch (e: Resources.NotFoundException) {
       Log.e(TAG, "Can't find style. Error: ", e)
   }
}
  1. 最后,在传入 GoogleMap 对象的 onMapReady() 方法中调用 setMapStyle() 方法。
override fun onMapReady(googleMap: GoogleMap) {
   ...
   setMapStyle(map)
}
  1. 运行应用。
  2. 将地图设为 normal 模式,系统应会显示采用复古主题和您所选道路颜色的新样式。

第 3 步:设置标记的样式

通过设置地图标记的样式,您可以进一步对地图进行个性化设置。在此步骤中,您将默认的红色标记更改为更有趣的内容。

  1. onMapLongClick() 方法中,将以下代码行添加到构造函数的 MarkerOptions() 中,以便使用默认标记,但将颜色更改为蓝色。
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE))

现在,onMapLongClickListener() 如下所示:

map.setOnMapLongClickListener { latLng ->
   // A snippet is additional text that's displayed after the title.
   val snippet = String.format(
       Locale.getDefault(),
       "Lat: %1$.5f, Long: %2$.5f",
       latLng.latitude,
       latLng.longitude
   )
   map.addMarker(
       MarkerOptions()
           .position(latLng)
           .title(getString(R.string.dropped_pin))
           .snippet(snippet)
         .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE))
   )
}
  1. 运行应用。长按后显示的标记现在会显示蓝色阴影。请注意,由于您未向 onPoiClick() 方法添加样式,因此 POI 标记仍显示为红色。

您可以自定义 Google 地图,其中一个方法是在 Google 地图上进行绘制。如果您想突出特定类型的地点(例如热门钓鱼地点),此方法就会有用。

  • 形状:您可以向地图添加多段线多边形圆形
  • GroundOverlay 对象:底面叠层是固定在地图上的图像。与标记不同,底面叠层朝向地球表面,而不是屏幕。旋转、倾斜或缩放地图会改变图片的朝向。如果您想将单个图片固定在地图上的某一区域,便可使用底面叠层。

步骤:添加底面叠层

在此任务中,您会将 Android 形状的底面叠层添加到您的住宅位置。

  1. 下载此 Android 图片并将其保存到您的 res/drawable 文件夹中。(请确保文件名为 android.png。)

  1. onMapReady() 中,在调用将相机移动到您的住宅位置后,创建一个 GroundOverlayOptions 对象。
  2. 将该对象分配给名为 androidOverlay 的变量。
val androidOverlay = GroundOverlayOptions()
  1. 通过 BitmapDescriptorFactory.fromResource() 方法,利用下载的图片资源创建 BitmapDescriptor 对象。
  2. 将生成的 BitmapDescriptor 对象传入 GroundOverlayOptions 对象的 image() 方法。
val androidOverlay = GroundOverlayOptions()
   .image(BitmapDescriptorFactory.fromResource(R.drawable.android))
  1. 为所需叠层的宽度(以米为单位)创建 float overlaySize。在本例中,100f 的宽度效果较好。

通过调用 position() 方法为 GroundOverlayOptions 对象设置 position 属性,并传入 homeLatLng 对象和 overlaySize

val overlaySize = 100f
val androidOverlay = GroundOverlayOptions()
   .image(BitmapDescriptorFactory.fromResource(R.drawable.android))
   .position(homeLatLng, overlaySize)
  1. GoogleMap 对象调用 addGroundOverlay() 并传入您的 GroundOverlayOptions 对象。
map.addGroundOverlay(androidOverlay)
  1. 运行应用。
  2. zoomLevel 的值更改为 18f,以查看叠层形式的 Android 图片。

用户经常使用 Google 地图查看自己的当前位置。如需在您的地图上显示设备位置,可以使用位置数据图层

位置数据层会向地图添加我的位置。当用户点按此按钮时,地图将以设备所在位置为中心。设备处于静止状态时,位置以蓝点显示;设备处于移动状态时,位置以蓝色 V 形显示。

在此任务中,您会启用位置数据图层。

步骤:请求位置信息权限

如需在 Google 地图中启用位置信息跟踪功能,只需一行代码即可。但是,您必须确保用户已授予位置信息权限(使用运行时权限模式)。

在此步骤中,您会请求位置信息权限并启用位置信息跟踪。

  1. AndroidManifest.xml 文件中,验证 FINE_LOCATION 权限是否已存在。当您选择了 Google 地图模板时,Android Studio 会插入此权限。
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  1. MapsActivity 中,创建一个 REQUEST_LOCATION_PERMISSION 类变量。
private val REQUEST_LOCATION_PERMISSION = 1
  1. 如需检查是否授予权限,请在 MapsActivity 中创建一个名为 isPermissionGranted() 的方法。在此方法中,检查用户是否已授予权限。
private fun isPermissionGranted() : Boolean {
  return ContextCompat.checkSelfPermission(
       this,
      Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
}
  1. 如需在应用中启用位置跟踪功能,请在 MapsActivity 中创建一个名为 enableMyLocation() 的方法,该方法不带参数也不返回任何内容。在里面,检查 ACCESS_FINE_LOCATION 权限。如果已授予该权限,请启用位置图层。否则,请申请该权限。
private fun enableMyLocation() {
   if (isPermissionGranted()) {
       map.isMyLocationEnabled = true
   }
   else {
       ActivityCompat.requestPermissions(
           this,
           arrayOf<String>(Manifest.permission.ACCESS_FINE_LOCATION),
           REQUEST_LOCATION_PERMISSION
       )
   }
}
  1. onMapReady() 回调中调用 enableMyLocation(),以启用位置图层。
override fun onMapReady(googleMap: GoogleMap) {
   ...
   enableMyLocation()
}
  1. 替换 onRequestPermissionsResult() 方法。如果 requestCode 等同于授予 REQUEST_LOCATION_PERMISSION 权限,并且 grantResults 数组不为空且 PackageManager.PERMISSION_GRANTED 在其第一个槽位中,则调用 enableMyLocation()
override fun onRequestPermissionsResult(
   requestCode: Int,
   permissions: Array<String>,
   grantResults: IntArray) {
   if (requestCode == REQUEST_LOCATION_PERMISSION) {
       if (grantResults.contains(PackageManager.PERMISSION_GRANTED)) {
           enableMyLocation()
       }
   }
}
  1. 运行应用。系统应会显示一个对话框,请求获取设备的位置信息。请继续并授予权限。

现在,地图会使用蓝点显示设备的当前位置。请注意,地图中有一个位置按钮。如果您移动地图以离开您所在的位置,然后点击此按钮,则系统会让地图重新以设备所在位置为中心。

下载已完成的 Codelab 的代码。

$  git clone https://github.com/googlecodelabs/android-kotlin-geo-maps


或者,您也可以下载 Zip 文件形式的代码库,将其解压缩并在 Android Studio 中打开。

下载 zip

  • 若要使用 Maps API,您需要使用 Google API 控制台中的 API 密钥。
  • 在 Android Studio 中,使用 Google Maps Activity 模板会在应用布局中生成一个 Activity,其中包含一个 SupportMapFragment。该模板还会将 ACCESS_FINE_PERMISSION 添加到应用清单中,在 activity 中实现 OnMapReadyCallback,并替换所需的 onMapReady() 方法。

如需在运行时更改 GoogleMap 的地图类型,请使用 GoogleMap.setMapType() 方法。Google 地图可为以下地图类型之一:

  • 正常:典型的道路地图。显示道路、人类建造的一些特征以及河流等重要的自然特征。此外,还会显示道路和景观标签。
  • 混合:添加了道路地图的卫星照片数据。此外,还会显示道路和景观标签。
  • 卫星:照片数据。不显示道路和景观标签。
  • 地形:地形数据。地图包含颜色、轮廓线和标签以及透视阴影。此外,还会显示一些道路和标签。
  • 无:没有基础地图图块。

关于 Google 地图:

  • 标记是特定地理位置的指示符。
  • 点按时,标记的默认行为是显示包含该位置相关信息的信息窗口。
  • 默认情况下,地图注点 (POI) 将与对应的图标一起显示在基本地图上。POI 包括公园、学校和政府大楼,等等。
  • 此外,如果地图类型为 normal,商家 POI(商店、餐馆、酒店等)会默认显示在地图上。
  • 您可以使用 OnPoiClickListener 获取 POI 的点击次数。
  • 您可以使用样式向导更改 Google 地图几乎所有元素的视觉外观。样式向导会生成 JSON 文件,您使用 setMapStyle() 方法将其传递到 Google 地图中。
  • 您可以通过更改默认颜色,或用自定义图像替换标记图标来自定义标记。

其他重要信息:

  • 使用底面叠层将图像固定在某个地理位置。
  • 使用 GroundOverlayOptions 对象指定图像、图像的大小(以米为单位)以及图像的位置。将此对象传递给 GoogleMap.addGroundOverlay() 方法以将叠层设置为地图。
  • 如果您的应用具有 ACCESS_FINE_LOCATION 权限,您可以通过设置 map.isMyLocationEnabled = true 来启用位置信息跟踪功能。
  • 此 Codelab 未介绍这些内容,但您可以使用 Google 街景(这是某个指定位置的可导航全景照片)提供有关某个位置的更多信息。

Android 开发者文档:

参考文档:

如需本课程中其他 Codelab 的链接,请参阅“使用 Kotlin 进行高级 Android 开发”Codelab 着陆页