相机和视图

请选择平台: 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,可用于按指定值增加缩放级别(如果值为负数,则降低缩放级别)。后一种方法可以固定屏幕上的指定点,让该点保留在同一位置(纬度/经度),为实现此操作,镜头的位置可能会更改。

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

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

      

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

显示大于视口的镜头 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 型数据。

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

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