相机和视图

请选择平台: Android iOS JavaScript

通过简单手势即可倾斜和旋转使用 Maps SDK for Android 制作的地图,从而让用户能够将地图调整到自己所需的方向。由于基于矢量的地图图块占据的空间较小,因此在任何缩放级别下平移地图或更改其视角都几乎不会产生延迟。

代码示例

GitHub 上的 ApiDemos 代码库包含一个展示相机功能的示例:

简介

与网页版 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 是当前的地图位置。生成的地图如下所示。

相机视角为 0 度、缩放级别为 18 的地图的屏幕截图。
以相机的默认视角显示的地图。
显示相机默认位置(在地图位置正上方,角度为 0)的示意图。
相机的默认视角。

以下图片中的视角为 45 度。请注意,相机沿圆弧移到了正上(0 度)与地面(90 度)中间的位置,也就是位置 3。相机仍指向地图的中心点,但现在位置 4 处的线表示的区域会显示出来。

相机视角为 45 度、缩放级别为 18 的地图的屏幕截图。
以 45 度视角显示的地图。
显示相机的视角设置为 45 度、缩放级别仍设置为 18 的示意图。
45 度相机视角。

此屏幕截图中的地图仍与原始地图采用同一个中心点,但地图顶部显示的地图项增加了。当您将角度增加到 45 度以上时,相机和地图位置之间的地图项会按比例显示得较大,而地图位置远处的地图项会按比例显示得较小,从而产生三维效果。

缩放级别

相机的缩放级别决定了地图的比例。缩放级别越大,屏幕上显示的信息越详细;缩放级别越小,屏幕上显示的世界范围越广。缩放级别为 0 时,地图的比例为:整个世界的宽度约为 256dp(密度无关像素)。

缩放级别每增加 1 个级别,屏幕上世界的宽度就会翻一番。因此,当缩放级别为 N 时,世界的宽度约为 256 * 2N dp。例如,缩放级别为 2 时,整个世界的宽度大约为 1024dp。

缩放级别的值不必是整数。地图允许的缩放级别范围取决于多个因素,包括目标、地图类型和屏幕尺寸。超出该范围的任何数值都将转换为最接近的有效值(可以是最小缩放级别或最大缩放级别)。以下列表显示了您在每个缩放级别看到的地图的大致详细程度:

  • 1:世界
  • 5:大陆/洲
  • 10:城市
  • 15:街道
  • 20:建筑物
以下图片显示了不同缩放级别的视觉外观:
缩放级别为 5 的地图的屏幕截图
缩放级别为 5 的地图。
缩放级别为 15 的地图的屏幕截图
缩放级别为 15 的地图。
缩放级别为 20 的地图的屏幕截图
缩放级别为 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,该类可按照指定值增加(如果值为负数,则降低)缩放级别。后一种方法可以修正屏幕上的指定点,让该点保留在同一位置(纬度/经度),为实现此操作,相机的位置可能会更改。

您可能会发现设置首选的最小和/或最大缩放级别非常有用。例如,如果您的应用显示地图注点周围的指定区域,或者如果您要将自定义图块叠加层与一组有限的缩放级别搭配使用,此设置有助于掌控用户体验。

Java


private GoogleMap map;
    map.setMinZoomPreference(6.0f);
    map.setMaxZoomPreference(14.0f);

      

Kotlin


private lateinit var map: GoogleMap

    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。

Java


LatLngBounds australiaBounds = new LatLngBounds(
    new LatLng(-44, 113), // SW bounds
    new LatLng(-10, 154)  // NE bounds
);
map.moveCamera(CameraUpdateFactory.newLatLngBounds(australiaBounds, 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))

      

将地图在给定区域内居中

在某些情况下,您可能希望将相机居中放置在边界内,而不是将极端边界包括在内。例如,在保持固定缩放值的情况下,将相机放置在某个国家/地区的中心位置。在这种情况下,您可以使用类似的方法:创建 LatLngBounds,并将 CameraUpdateFactory.newLatLngZoom(LatLng latLng, float zoom)LatLngBounds.getCenter() 方法结合使用。getCenter() 方法会返回 LatLngBounds 的地理中心。

Java


LatLngBounds australiaBounds = new LatLngBounds(
    new LatLng(-44, 113), // SW bounds
    new LatLng(-10, 154)  // NE bounds
);
map.moveCamera(CameraUpdateFactory.newLatLngZoom(australiaBounds.getCenter(), 10));

      

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

      

使用方法 newLatLngBounds(boundary, width, height, padding) 的重载,您可以指定矩形的宽度和高度(以像素为单位),并使宽度和高度符合地图的尺寸。系统会对该矩形进行定位,使其中心与地图视图的中心一致(这样,如果指定的尺寸与地图视图的尺寸一致,那么该矩形就会与地图视图一致)。返回的 CameraUpdate 会相应地移动相机,以使指定的 LatLngBounds 在计入所需内边距后,能够在可能的最高缩放级别下在屏幕上的给定矩形内居中显示。

注意:仅当要在设置地图布局后使用 CameraUpdate 移动相机时,才能使用更简单的方法 newLatLngBounds(boundary, padding) 生成该类。在设置布局的过程中,API 会计算地图的显示边界,因为系统需要这些边界来正确投影边界框。相比之下,您可以随时使用由较复杂的方法 newLatLngBounds(boundary, width, height, padding) 返回的 CameraUpdate,此方法甚至可在地图设置好布局之前使用,因为 API 会通过您传递的参数计算显示边界。

将用户的平移操作限定在指定区域

在上述情况下,您虽然设置了地图的边界,但是用户仍可以滚动或平移至这些边界外。您不妨改为限制地图焦点(相机目标)的纬度/经度中心边界,以便用户只能在这些边界内滚动和平移。例如,购物中心或机场的零售应用可能希望将地图限制到特定边界,使用户只能在这些边界内滚动和平移。

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

      

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)

      

在下图所示的情况下,相机目标被限制在一个略微大于视口的区域内。只要相机仍然位于边界区域内,用户就可以滚动和平移。叉型记号表示相机目标:

显示大于视口的相机 LatLngBounds 的图片。

地图将始终填满视口,即使这会导致视口显示指定边界以外的区域也是如此。例如,如果您将相机目标定位在边界区域的角上,超出角的区域在视口中仍然可见,但是用户无法滚动到该区域。下图说明了这种情况。叉型记号表示相机目标:

显示相机目标定位在相机 LatLngBounds 右下角的图片。

在下图中,相机目标具有一个非常有限的边界,这让用户基本无法滚动或平移地图。叉型记号表示相机目标:

显示小于视口的相机 LatLngBounds 的图片。

更新相机视图

要为地图应用 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 型数据。

下面的代码段说明了移动相机的一些常用方法。

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

      

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