通过简单手势即可倾斜和旋转使用 Maps SDK for Android 制作的地图,从而让用户能够将地图调整到自己所需的方向。由于基于矢量的地图图块占据的空间较小,因此在任何缩放级别下平移地图或更改其视角都几乎不会产生延迟。
代码示例
GitHub 上的 ApiDemos 代码库包含一个展示镜头功能的示例:
- CameraDemoActivity - Kotlin:更改镜头位置
- CameraDemoActivity - Java:更改镜头位置
简介
与网页版 Google 地图一样,Maps SDK for Android 也使用墨卡托投影法在设备屏幕(平面)上呈现地球的表面(球面)。由于地球是无缝衔接的球体,因此在东西方向上,地图会不断地重复。而在南北方向上,地图覆盖的纬度上限大约为南北纬 85 度。
注意:墨卡托投影法在经度方向上的宽度有限,但在纬度方向上的高度无限。我们利用墨卡托投影法在大约 +/-85 度处将基本地图图像截断,使制作出的地图呈方形,从而简化图块选择逻辑。
借助 Maps SDK for Android,您可以通过修改地图的镜头来更改用户查看地图的视角。
对镜头所做的更改不会使标记、叠加层或您添加的其他图形发生变化,但您可能需要更改所添加的内容,使其更适合新视图。
您可以监听用户在地图上所做的手势,从而更改地图以响应用户请求。例如,回调方法 OnMapClickListener.onMapClick()
可响应地图上的单次点按。通过该方法可获得点按位置的纬度和经度,因此您可以通过平移或缩放至该点来做出响应。还有一些类似的方法可用于响应标记提示框中的点按操作,或响应标记上的拖动手势。
您还可以监听镜头移动情况,以便您的应用可以在镜头开始移动、当前正在移动或停止移动时收到通知。有关详情,请参阅有关镜头更改事件的指南。
镜头位置
地图视图被建模成一个俯视某个平面的镜头。下列属性指定了镜头的位置(也进而决定了地图的渲染效果):目标(纬度/经度位置)、方向角、倾斜度和缩放级别。
目标(位置)
镜头目标是地图中心的位置,以纬度和经度坐标形式指定。
纬度可以介于 -85 度(含)和 85 度(含)之间。不在此范围内的值会被调整为此范围内最接近的值。例如,如果将纬度指定为 100,系统会将该值设为 85。经度范围为 -180 度(含)到 180 度(含)。不在此范围内的值会被换算为 (-180, 180) 范围内的值。例如,480、840 和 1200 都会换算为 120 度。方向角(方向)
镜头方向角指定了罗盘方向,以相对于正北方(对应于地图的顶部边缘)的角度来表示。从地图中心到顶部边缘画一条垂直线,镜头朝向相对于正北方的角度(以度为单位)即为方向角。
方向角为 0 表示地图顶部指向正北。方向角为 90 表示地图顶部指向正东方(罗盘上为 90 度)。方向角为 180 表示地图顶部指向正南方。
您可以使用 Maps API 更改地图的方向角。例如,驾车的用户通常会旋转道路地图使其与他们的行驶方向一致,而远足者使用地图和罗盘时通常会调整地图方向,使地图上有一条垂直线指向北方。
倾斜度(视角)
倾斜度是指镜头在地图中心位置正上方圆弧上所处的位置,以相对于天底(相机正下方)的角度来表示。值为 0 时,镜头竖直朝下。值大于 0 时,镜头按指定角度朝地平线倾斜。更改视角时,地图会以透视法呈现:远处的地图项显示得较小,而近处的地图项显示得较大。以下插图展示了这种情况。
在下面的图片中,视角为 0 度。第一张图片是视角为 0 度时的示意图;位置 1 是镜头位置,位置 2 是当前的地图位置。生成的地图如下所示。
以下图片中的视角为 45 度。请注意,镜头沿圆弧移到了正上(0 度)与地面(90 度)中间的位置,也就是位置 3。镜头仍指向地图的中心点,但现在位置 4 处的线表示的区域会显示出来。
此屏幕截图中的地图仍与原始地图采用同一个中心点,但地图顶部显示的地图项增加了。当您将角度增加到 45 度以上时,镜头和地图位置之间的地图项会按比例显示得较大,而地图位置远处的地图项会按比例显示得较小,从而产生三维效果。
缩放级别
镜头的缩放级别决定了地图的比例。缩放级别越大,屏幕上显示的信息越详细;缩放级别越小,屏幕上显示的范围越广。缩放级别为 0 时,地图的比例为:整个地球的宽度约为 256dp(密度无关像素)。
缩放级别每增加 1 个级别,屏幕上地球的宽度就会翻一番。因此,当缩放级别为 N 时,地球的宽度约为 256 * 2N dp。例如,缩放级别为 2 时,整个地球的宽度大约为 1024dp。
缩放级别的值不必是整数。地图允许的缩放级别范围取决于多个因素,包括目标、地图类型和屏幕尺寸。超出该范围的任何数值都将转换为最接近的有效值(可以是最小缩放级别或最大缩放级别)。以下列表显示了您在每个缩放级别看到的地图的大致详细程度:
- 1:世界
- 5:大陆/洲
- 10:城市
- 15:街道
- 20:建筑物
移动镜头
Maps API 可让您更改在地图上显示世界的哪一部分区域。不必移动地图,更改镜头的位置即可达到此目的。
更改镜头位置时,可以选择以动画方式呈现镜头移动的效果。动画会插入当前镜头属性和新的镜头属性之间。您还可以控制动画的时长。
如需更改镜头位置,您必须利用 CameraUpdate
指定您想将镜头移动至何处。借助 Maps API,您可以使用 CameraUpdateFactory
创建多种不同类型的 CameraUpdate
。提供的选项如下:
更改缩放级别和设置最小/最大缩放级别
CameraUpdateFactory.zoomIn()
和 CameraUpdateFactory.zoomOut()
提供了 CameraUpdate
,可用于按 1.0 的幅度更改缩放级别,同时让其他所有属性保持不变。
CameraUpdateFactory.zoomTo(float)
提供了 CameraUpdate
,可用于将缩放级别的值更改为指定值,同时让所有其他属性保持不变。
CameraUpdateFactory.zoomBy(float)
和 CameraUpdateFactory.zoomBy(float, Point)
提供了 CameraUpdate
,可用于按指定值增加缩放级别(如果值为负数,则降低缩放级别)。后一种方法可以固定屏幕上的指定点,让该点保留在同一位置(纬度/经度),为实现此操作,镜头的位置可能会更改。
您可能会发现设置首选的最小和/或最大缩放级别非常有用。例如,如果您的应用显示地图注点周围的指定区域,或者如果您要将自定义图块叠加层与一组有限的缩放级别搭配使用,此设置有助于掌控用户体验。
Kotlin
private lateinit var map: GoogleMap map.setMinZoomPreference(6.0f) map.setMaxZoomPreference(14.0f)
Java
private GoogleMap map; map.setMinZoomPreference(6.0f); map.setMaxZoomPreference(14.0f);
请注意,出于技术方面的考虑,可能会阻止 API 允许用户过低或过高地进行缩放。例如,卫星地图或地形地图图块的最大缩放级别可能低于基本地图图块。
更改镜头位置
用户可以通过两种简便的方法执行常见的位置更改操作。CameraUpdateFactory.newLatLng(LatLng)
提供了 CameraUpdate
,可用于更改镜头的纬度和经度,同时让所有其他属性保持不变。CameraUpdateFactory.newLatLngZoom(LatLng, float)
提供了 CameraUpdate
,可用于更改镜头的纬度、经度和缩放级别,同时让所有其他属性保持不变。
要想以最灵活的方式更改镜头位置,请使用 CameraUpdateFactory.newCameraPosition(CameraPosition)
,其中的 CameraUpdate
可用于将镜头移至指定位置的。用户可直接通过 new CameraPosition()
或借助使用 new CameraPosition.Builder()
的 CameraPosition.Builder
获取 CameraPosition
。
平移(滚动)
CameraUpdateFactory.scrollBy(float, float)
提供了 CameraUpdate
,可用于更改镜头的纬度和经度,以使地图按指定的像素数移动。x 正值会将镜头向右移动,显示出来的效果就是地图向左移动。y 正值会将镜头向下移动,显示出来的效果就是地图向上移动。相反,x 负值会将镜头会向左移动,显示出来的效果就是地图向右移动,而 y 负值则会将镜头向上移动。滚动方向是相对于镜头当前位置而言的。例如,如果镜头的方向角为 90 度,那么“上”方就表示东方。
设置边界
设置地图的边界
有时,您可以移动镜头,使自己感兴趣的整个区域以尽可能最高的缩放级别显示,这一点非常实用。例如,如果您要显示用户当前所在位置五英里内的所有加油站,则您可能需要移动镜头,以使所有加油站都显示在屏幕上。为此,请先计算您想在屏幕上显示的 LatLngBounds
。然后使用 CameraUpdateFactory.newLatLngBounds(LatLngBounds bounds, int
padding)
获取 CameraUpdate
,它可以更改镜头位置,以使指定的 LatLngBounds
完全适合地图。更改镜头时需将指定的内边距(以像素为单位)考虑在内。返回的 CameraUpdate
可以确保指定边界和地图边缘之间的间距(以像素为单位)不小于指定的内边距。请注意,地图倾斜度和方向角的值将都为 0。
Kotlin
val australiaBounds = LatLngBounds( LatLng((-44.0), 113.0), // SW bounds LatLng((-10.0), 154.0) // NE bounds ) map.moveCamera(CameraUpdateFactory.newLatLngBounds(australiaBounds, 0))
Java
LatLngBounds australiaBounds = new LatLngBounds( new LatLng(-44, 113), // SW bounds new LatLng(-10, 154) // NE bounds ); map.moveCamera(CameraUpdateFactory.newLatLngBounds(australiaBounds, 0));
将地图在给定区域内居中
在某些情况下,您可能希望将镜头居中放置在边界内,而不是将极端边界包括在内。例如,在保持固定缩放值的情况下,将镜头放置在某个国家/地区的中心位置。在这种情况下,您可以使用类似的方法:创建 LatLngBounds
,并将 CameraUpdateFactory.newLatLngZoom(LatLng latLng, float zoom)
与 LatLngBounds
.getCenter()
方法结合使用。getCenter() 方法会返回 LatLngBounds
的地理中心。
Kotlin
val australiaBounds = LatLngBounds( LatLng((-44.0), 113.0), // SW bounds LatLng((-10.0), 154.0) // NE bounds ) map.moveCamera(CameraUpdateFactory.newLatLngZoom(australiaBounds.center, 10f))
Java
LatLngBounds australiaBounds = new LatLngBounds( new LatLng(-44, 113), // SW bounds new LatLng(-10, 154) // NE bounds ); map.moveCamera(CameraUpdateFactory.newLatLngZoom(australiaBounds.getCenter(), 10));
使用方法 newLatLngBounds(boundary, width, height,
padding)
的重载,您可以指定矩形的宽度和高度(以像素为单位),并使宽度和高度符合地图的尺寸。系统会对该矩形进行定位,使其中心与地图视图的中心一致(这样,如果指定的尺寸与地图视图的尺寸一致,那么该矩形就会与地图视图一致)。返回的 CameraUpdate
会相应地移动镜头,以使指定的 LatLngBounds
在计入所需内边距后,能够在可能的最高缩放级别下在屏幕上的给定矩形内居中显示。
注意:仅当要在设置地图布局后使用 CameraUpdate
移动镜头时,才能使用更简单的方法 newLatLngBounds(boundary, padding)
生成该类。在设置布局的过程中,API 会计算地图的显示边界,因为系统需要这些边界来正确投影边界框。相比之下,您可以随时使用由较复杂的方法 newLatLngBounds(boundary, width, height, padding)
返回的 CameraUpdate
,此方法甚至可在地图设置好布局之前使用,因为 API 会通过您传递的参数计算显示边界。
将用户的平移操作限定在指定区域
在上述情况下,您虽然设置了地图的边界,但是用户仍可以滚动或平移至这些边界外。您不妨改为限制地图焦点(镜头目标)的纬度/经度中心边界,以便用户只能在这些边界内滚动和平移。例如,购物中心或机场的零售应用可能希望将地图限制在特定边界以内,让用户只能在这些边界内滚动和平移。
Kotlin
// Create a LatLngBounds that includes the city of Adelaide in Australia. val adelaideBounds = LatLngBounds( LatLng(-35.0, 138.58), // SW bounds LatLng(-34.9, 138.61) // NE bounds ) // Constrain the camera target to the Adelaide bounds. map.setLatLngBoundsForCameraTarget(adelaideBounds)
Java
// Create a LatLngBounds that includes the city of Adelaide in Australia. LatLngBounds adelaideBounds = new LatLngBounds( new LatLng(-35.0, 138.58), // SW bounds new LatLng(-34.9, 138.61) // NE bounds ); // Constrain the camera target to the Adelaide bounds. map.setLatLngBoundsForCameraTarget(adelaideBounds);
在下图所示的情况下,镜头目标被限制在一个略微大于视口的区域内。只要镜头目标仍然位于边界区域内,用户就可以滚动和平移。叉型记号表示镜头目标:
地图将始终填满视口,即使这会导致视口显示指定边界以外的区域也是如此。例如,如果您将镜头目标定位在边界区域的角上,超出角的区域在视口中仍然可见,但是用户无法滚动到该区域。下图说明了这种情况。叉型记号表示镜头目标:
在下图中,镜头目标具有一个非常有限的边界,这让用户基本无法滚动或平移地图。叉型记号表示镜头目标:
更新镜头视图
要为地图应用 CameraUpdate
,您可以立即移动镜头,或以动画方式平稳地移动镜头。要立即使用指定的 CameraUpdate
移动镜头,您可以调用 GoogleMap.moveCamera(CameraUpdate)
。
不妨以动画形式呈现所做的更改,让用户享受更愉悦的体验,特别是在短距离移动方面。为此,请不要调用 GoogleMap.moveCamera
,而应调用 GoogleMap.animateCamera
。地图会平稳地移至新属性。此方法最具体的形式 GoogleMap.animateCamera(cameraUpdate, duration, callback)
提供了以下三种参数:
cameraUpdate
CameraUpdate
用于说明将镜头移至哪个位置。callback
- 这是一个用于实现
GoogleMap.CancellableCallback
的对象。这个用于处理任务的通用接口定义了 `onCancel()` 和 `onFinished()` 这两种方法。对于动画,调用这两种方法的情况分别如下:onFinish()
- 如果动画完整播放且没有中断,系统会调用该方法。
onCancel()
-
如果调用
stopAnimation()
或执行新的镜头移动操作会造成动画播放中断,系统会调用该方法。此外,如果您调用
GoogleMap.stopAnimation()
,也会出现上述情况。
duration
- 动画播放所需的时长,以毫秒为单位,为
int
型数据。
下面的代码段说明了移动镜头的一些常用方法。
Kotlin
val sydney = LatLng(-33.88, 151.21) val mountainView = LatLng(37.4, -122.1) // Move the camera instantly to Sydney with a zoom of 15. map.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney, 15f)) // Zoom in, animating the camera. map.animateCamera(CameraUpdateFactory.zoomIn()) // Zoom out to zoom level 10, animating with a duration of 2 seconds. map.animateCamera(CameraUpdateFactory.zoomTo(10f), 2000, null) // Construct a CameraPosition focusing on Mountain View and animate the camera to that position. val cameraPosition = CameraPosition.Builder() .target(mountainView) // Sets the center of the map to Mountain View .zoom(17f) // Sets the zoom .bearing(90f) // Sets the orientation of the camera to east .tilt(30f) // Sets the tilt of the camera to 30 degrees .build() // Creates a CameraPosition from the builder map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
Java
LatLng sydney = new LatLng(-33.88,151.21); LatLng mountainView = new LatLng(37.4, -122.1); // Move the camera instantly to Sydney with a zoom of 15. map.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney, 15)); // Zoom in, animating the camera. map.animateCamera(CameraUpdateFactory.zoomIn()); // Zoom out to zoom level 10, animating with a duration of 2 seconds. map.animateCamera(CameraUpdateFactory.zoomTo(10), 2000, null); // Construct a CameraPosition focusing on Mountain View and animate the camera to that position. CameraPosition cameraPosition = new CameraPosition.Builder() .target(mountainView ) // Sets the center of the map to Mountain View .zoom(17) // Sets the zoom .bearing(90) // Sets the orientation of the camera to east .tilt(30) // Sets the tilt of the camera to 30 degrees .build(); // Creates a CameraPosition from the builder map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));