相機和檢視畫面

透過簡單的手勢就能傾斜和旋轉 Maps SDK for Android 中的地圖,方便使用者將地圖調整至適當的方向。任何縮放等級都可以平移地圖或變更其視角,而且因為向量地圖圖塊的記憶體使用量較小,因此很少會有延遲情況。

程式碼範例

GitHub 上的 ApiDemos 存放區有相機功能的說明範例:

簡介

和網路版 Google 地圖一樣,Maps SDK for Android 使用麥卡托投影,在裝置的螢幕 (平面) 上呈現地球表面 (球體)。地圖會朝東西向無限重複,因為地球本身就是無縫環繞的球體。地圖的南北向則限制在大約北緯 85 度和南緯 85 度以內。

注意:麥卡托投影的經度寬度有限制,但緯度高度則沒有限制。我們利用麥卡托投影在大約 +/- 85 度的位置「裁切」基本地圖圖像,讓最終地圖呈正方形,以簡化選取圖塊的方式。

Maps SDK for Android 可讓您藉由調整地圖的相機,變更使用者的地圖視角。

調整相機不會變更已加入的標記、疊加層或其他圖形,不過建議您配合新視角修改加入的項目,呈現精確的地圖。

由於您可以監聽地圖上的使用者手勢,因此可以配合使用者要求來變更地圖。舉例來說,回呼方法 OnMapClickListener.onMapClick() 會回應地圖上的一次輕觸。此方法會接收輕觸位置的緯度與經度,所以您可以藉由平移或縮放至該點來回應。您可以利用其他類似方法,在使用者輕觸標記泡泡或對標記做出拖曳手勢時進行回應。

您也可以監聽相機動作,這樣當相機開始移動、正在移動或停止移動時,應用程式就會收到通知。詳情請參閱相機變更事件指南。

地圖上的 3D 建築物

近距離查看許多城市時,地圖會顯示 3D 建築物,如下方加拿大溫哥華的圖片所示。您可以呼叫 GoogleMap.setBuildingsEnabled(false) 來停用 3D 建築物。

加拿大溫哥華的地圖

相機位置

地圖檢視的視角模擬向下俯瞰平面的相機。相機的位置 (以及連帶的地圖算繪方式) 是由下列屬性來指定:目標 (經緯度位置)航向角度傾斜度縮放

相機屬性圖表

目標 (位置)

攝影機目標是地圖的中心位置,透過經緯度座標指定。

航向 (方向)

相機航向是指以度為單位,從正北方順時針測量至地圖點上垂直的線條所得的角度。汽車駕駛常常會翻轉道路地圖,使地圖與其行進方向一致;但同時使用地圖和指南針的健行者,則通常會讓地圖上的垂直線保持指向北方。Maps API 能讓您改變地圖的對齊方式或航向。舉例來說,若航向設為 90 度,地圖的上方就會指向正東方。

傾斜度 (視角)

傾斜度是指在地圖中心位置正上方和地球表面之間的弧形上,從天底 (相機正下方的方向) 測量至相機位置所得的值 (單位為度)。當您改變視角時,地圖會以視角呈現,較遠的地圖項目看起來較小,鄰近的地圖項目看起來則較大。請參閱下方範例的說明。

在下圖中,視角為 0 度。第一張圖是相關示意圖,1 是相機位置,2 則是目前地圖的位置。最終地圖則如下所示。

地圖螢幕截圖,相機採 0 度視角,縮放等級為 18。
以相機預設視角呈現的地圖。
顯示相機預設位置的圖表,位於地圖位置正上方,角度為 0 度。
相機的預設視角。

在下圖中,視角為 45 度。請注意,相機不會傾斜 45 度,而是在地圖正上方 (0 度) 和地面 (90 度) 之間的弧形上移動至中間,也就是 3 的位置。 相機仍然會指向地圖的中心點,但位置 4 的線條代表的區域現在就會出現在地圖中。

地圖螢幕截圖,相機採 45 度視角,縮放等級為 18。
以 45 度視角呈現的地圖。
顯示相機視角為 45 度而縮放等級仍設為 18 的圖表。
45 度的相機視角。

在此螢幕截圖中,地圖的中心點仍與原始地圖相同,但地圖頂端顯示了更多地圖項目。若您將視角調整至 45 度以上,相機和地圖位置之間的地圖項目看起來會較大,而地圖位置以外的地圖項目則看起來較小,因而產生 3D 效果。

縮放

地圖比例取決於相機的縮放等級。縮放等級較大時,螢幕上會顯示較多細節;縮放等級較小時,則能在螢幕上顯示較大的範圍。縮放等級為 0 時,地圖的尺寸是整個世界的寬度大約為 256dp (密度獨立像素)。

將縮放等級提升 1,螢幕上的世界寬度就會加倍。因此,縮放等級若為 N,世界的寬度就大約是 256 * 2N dp;舉例來說,縮放等級為 2,整個世界的寬度大約是 1024dp。注意,縮放等級不需要是整數。地圖允許的縮放等級範圍取決於許多因素,包括位置、地圖類型和螢幕大小。範圍外的任何數字都會轉換為下一個最接近的有效值,可能是最小或最大縮放等級。以下清單列出各縮放等級大致可顯示的精細程度:

  • 1:全世界
  • 5:自然景觀/大陸
  • 10:城市
  • 15:街道
  • 20:建築

下圖顯示了不同縮放等級的視覺外觀:

縮放等級為 5 的地圖螢幕截圖
縮放等級為 5 的地圖。
縮放等級為 15 的地圖螢幕截圖
縮放等級為 15 的地圖。
縮放等級為 20 的地圖螢幕截圖
縮放等級為 20 的地圖。

注意:視螢幕大小和密度而定,部分裝置可能不支援最低的縮放等級。您可以使用 GoogleMap.getMinimumZoomLevel() 取得地圖的最低縮放等級。如果需要在可視區域中顯示整個世界,建議您使用精簡模式

移動相機

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(),或搭配 CameraPosition.Builder 使用 new 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 並搭配使用 LatLngBoundsCameraUpdateFactory.newLatLngZoom(LatLng latLng, float zoom)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 以最大縮放等級置於螢幕上指定矩形的中心,同時將要求的邊框間距納入考量。

注意:只有需要在地圖完成版面配置後移動相機時,才使用較簡單的方法 newLatLngBounds(boundary, padding) 來產生 CameraUpdate。在版面配置期間,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))