創建一個安卓應用 - Amazon Location Service

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

創建一個安卓應用

在本節中,您將創建一個帶有地圖的 Android 應用程序,可以在一個位置進行搜索並在前台跟踪。首先,您將為您的應用程式建立您的 Amazon 位置資源、Amazon Cognito 身分和 API 金鑰。

為您的應用程式建立 Amazon 位置資源

如果您還沒有這些資源,則必須建立應用程式將使用的 Amazon 位置資源。在這裡,您可以創建一個地圖資源以在應用程序中顯示地圖,用於在地圖上搜索位置的位置索引,以及用於在地圖上跟踪對象的跟踪器。

將位置資源新增至您的應用程式
  1. 選擇您要使用的地圖樣式。

    1. 在 Amazon 位置主控台的「地」頁面上,選擇「建立地圖」以預覽地圖樣式。

    2. 為新地圖資源新增「名稱」和「描述」。記下您用於地圖資源的名稱。稍後在自學課程中建立腳本檔案時,您將需要它。

    3. 我們建議您為您的地圖選擇 HERE 地圖。

      注意

      選擇地圖型式也會選擇您將使用的地圖資料提供者。如果您的應用程式正在追蹤或路由您在業務中使用的資產,例如送貨車輛或員工,您只能使用 HERE 作為您的地理位置供應商。如需詳細資訊,請參閱AWS 服務條款第 82 節。

    4. 同意 Amazon 位置條款與條件,然後選擇「建立地圖」。您可以與選擇的地圖互動:放大、縮小或向任何方向平移。

    5. 記下為您的新地圖資源顯示的 Amazon 資源名稱(ARN)。您將在本教程稍後使用它來創建正確的身份驗證。

  2. 選擇您要使用的位置索引。

    1. 在「放置索引」頁面上的 Amazon 位置主控台中,選擇「建立位置索引」。

    2. 新增位置索引資源的「名稱」和「說明」。記下您用於放置索引資源的名稱。稍後在自學課程中建立腳本檔案時,您將需要它。

    3. 選擇資料提供者。

      注意

      在大多數情況下,請選擇與您已選擇的地圖提供者相符的資料提供者。這有助於確保搜索將匹配地圖。

      如果您的應用程式正在追蹤或路由您在業務中使用的資產,例如送貨車輛或員工,您只能使用 HERE 作為您的地理位置供應商。如需詳細資訊,請參閱AWS 服務條款第 82 節。

    4. 選擇數據存儲選項。在本自學課程中,不會儲存結果,因此您可以選擇「否,僅限單次使用」。

    5. 同意 Amazon 地點條款與條件,然後選擇建立地點索引

    6. 記下針對新位置索引資源顯示的 ARN。您將在本教程的下一部分中使用它來創建正確的身份驗證。

  3. 使用 Amazon 位置控制台創建跟踪器。

    1. 打開 Amazon 定 Location Service 控制台

    2. 在左側導覽窗格中,選擇「追蹤器」。

    3. 選擇建立追蹤器

    4. 填寫所有必填欄位。

    5. 在「位置篩選」下,我們建議您使用預設設定:TimeBased

    6. 選擇創建跟踪器以完成。

為您的應用程式設定驗證

您在本教程中創建的應用程序具有匿名用法,這意味著您的用戶不需 AWS 要登錄即可使用該應用程序。但是,Amazon 定 Location Service API 需要身份驗證才能使用。您可以使用 API 金鑰或 Amazon Cognito 為匿名使用者提供身份驗證和授權。本教學將使用 Amazon Cognito 和 API 金鑰來驗證您的應用程式。

注意

如需將 Amazon Cognito 或 API 金鑰與 Amazon 定 Location Service 搭配使用的詳細資訊,請參閱授予對 Amazon 定 Location Service 的訪問

以下教學課程說明如何設定地圖、位置索引和您在其中建立的追蹤器的身份驗證,以及如何設定 Amazon 位置的許可。

設定驗證
  1. 導覽至 Amazon 位置主控台,然後從左側功能表中選取 API 金鑰

  2. 點擊「創建 API 密鑰」。請記住,API 金鑰必須與先前建立的 Amazon 定位服務資源位於相同的 AWS 帳戶和區域。

  3. 在「創建 API 密鑰」頁面上填寫所需的詳細信息:

    • 名稱:提供 API 金鑰的名稱,例如MyAppKey

    • 資源:選擇先前建立的 Amazon 定 Location Service 地圖和放置索引資源。您可以通過選擇「添加資源」添加多個資源。這允許 API 密鑰與指定的資源一起使用。

    • 動作:指定此 API 金鑰的授權動作。至少選取geo:GetMapgeo:SearchPlaceIndexForPosition以確保自學課程如預期般運作。

    • 選用您可以新增說明、到期時間、標籤或參照者,例如https://www.example.com,將金鑰的使用量限制在特定網域,讓教學課程只能在該網域內運作。

  4. 按一下「建立 API 金鑰」以產生 API 金鑰。

  5. 選取 [顯示 API 金鑰] 並複製金鑰值,以v1.public.a1b2c3d4供稍後在教學課程中使用。

建立 IAM 政策以進行追蹤
  1. 以具有管理員許可的使用者身分登入 IAM 主控台 (https://console.aws.amazon.com/iam/)。

  2. 在導覽窗格上選擇 Policies (政策)。

  3. 在內容窗格中,選擇 Create policy (建立政策)。

  4. 選擇 JSON 選項,然後將此 JSON 政策複製並貼到 JSON 文字方塊中。

    { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "geo:GetMapTile", "geo:GetMapStyleDescriptor", "geo:GetMapSprites", "geo:GetMapGlyphs", "geo:SearchPlaceIndexForPosition", "geo:GetDevicePositionHistory", "geo:BatchUpdateDevicePosition" ], "Resource": [ "arn:aws:geo:{Region}:{Account}:map/{MapName}", "arn:aws:geo:{Region}:{Account}:place-index/{IndexName}", "arn:aws:geo:{Region}:{Account}:tracker/{TrackerName}" ] } ] }

    這是追蹤的政策範例。若要將範例用於您自己的原則,請取代RegionAccount、和TrackerName預留位置。

    注意

    雖然未驗證身分集區的目的是在不安全的網際網路網站上曝光,但請注意,這些身分集區會被交換成標準且有時間限制的 AWS 登入資料。

    適當範圍與未驗證身分集區相關聯的 IAM 角色非常重要。如需有關在 Amazon Cognito 與 Amazon 定 Location Service 務搭配使用和適當設定政策範圍的詳細資訊,請參閱授與 Amazon 定 Location Service 的存取權

  5. 在「檢閱並建立」頁面上,輸入原則名稱欄位的名稱。檢閱原則所授與的權限,然後選擇 [建立原則] 儲存工作。

新的政策會出現在受管政策清單中,並且已準備好連接。

設定追蹤的驗證
  1. Amazon Cognito 主控台中為您的地圖應用程式設定身份驗證。

  2. 開啟 [識別集區] 頁面。

    注意

    您建立的集 AWS 區必須與您在上一節中建立的 Amazon 定 Location Service 資源位於相同的 AWS 帳戶和區域。

  3. 選擇 [建立識別集區]。

  4. [設定識別集區信任] 步驟開始。對於使用者存取驗證,請選取來賓存取,然後按下一步。

  5. 在 [設定權限] 頁面上,選取 [使用現有的 IAM 角色],然後輸入您在上一步中建立的 IAM 角色名稱。準備就緒後,按下一步繼續進行下一步。

  6. [設定內容] 頁面上,提供身分識別集區的名稱。然後按下一頁.

  7. 在 [檢閱並建立] 頁面上,檢閱存在的所有資訊,然後按 [建立身分集區]。

  8. 開啟 [身分識別集區] 頁面,然後選取您剛建立的身分集區。然後複製或寫下 IdentityPoolId 您稍後將在瀏覽器腳本中使用的內容。

創建基本的安卓應用程序

在本教程中,您將創建一個嵌入地圖的 Android 應用程序,並允許用戶查找地圖上的位置。

首先,使用 Android 工作室的新項目嚮導創建一個空的 Kotlin 應用程序。

若要建立空白應用程式 (AndroidStudio)
  1. 開始 AndroidStudio。打開菜單,然後選擇文件新建新建項目

  2. 從 [電話和平板電腦] 索引標籤中,選取 [空白活動],然後選擇 [下一步

  3. 為您的應用程式選擇「名稱」、「Pack age 名稱」和「儲存位置」。

  4. 語言的下拉列表中,選擇 Kotlin

  5. 選擇「完成」以建立空白應用程式。

將交互式地圖添加到您的應用程序

現在您已經建立了基本的應用程式,您可以將地圖控制項新增至您的應用程式。本教學課程使用 API 金鑰來管理地圖檢視。地圖控件本身是本MapLibre 機庫的一部分,使用 API 密鑰和 MapLibre,地圖數據來自 Amazon 位置。

要將映射添加到您的應用程序,您必須執行以下操作:

  • 將 MapLibre 依賴項添加到您的項目中。

  • 使用撰寫設定地圖檢視程式碼。

  • 編寫代碼以顯示地圖。

使用以下步驟將地圖添加到您的應用程序:

  1. 將 MapLibre 依賴項添加到您的項目

    1. 在中 AndroidStudio,選取「檢視」功能表,然後選擇「工具視窗」、「專案」。這將打開項目窗口,該窗口使您可以訪問項目中的所有文件。

    2. 在「項目」窗口中,打開 gradle,然後在樹視圖中打開libs.versions.toml文件。這將打開libs.versions.toml文件進行編輯。現在在libs.versions.toml文件中添加以下版本和庫數據。

      [versions] ... auth = "0.2.4" tracking = "0.2.4" [libraries] ... auth = { group = "software.amazon.location", name = "auth", version.ref = "auth" } tracking = { module = "software.amazon.location:tracking", version.ref = "tracking" } [plugins] ...
    3. 完成編輯libs.versions.toml檔案後, AndroidStudio 必須重新同步專案。在編libs.versions.toml輯視窗頂端, AndroidStudio 會提示您同步。選擇「立即同步」以同步您的項目,然後再繼續。

    4. 在「項目」窗口中,在樹視圖中打開 Gradle 腳本,然後選擇應用程序模塊的build.gradle文件。這將打開build.gradle文件進行編輯。

    5. 在檔案底部的 [相依性] 區段中,新增下列相依性。

      dependencies { ... implementation(libs.org.maplibre.gl) }
    6. 完成添加搖籃依賴關係後,Android 工作室必須重新同步該項目。在編輯窗口的頂部,Android Studio 中,選擇立即同步以同步您的項目,然後再繼續

  2. 現在,您將使用撰寫設置地圖視圖代碼。使用下列步驟:

    1. 從項目窗口中,在樹視圖中打開 App,Java,您的包名稱,然後轉到 ui 文件夾,在 ui 文件夾中創建一個視圖目錄。

    2. 裡面的視圖目錄創建一個MapLoadScreen.kt文件。

    3. 將以下代碼添加到您的MapLoadScreen.kt文件中。

      import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.viewinterop.AndroidView import org.maplibre.android.maps.OnMapReadyCallback @Composable fun MapLoadScreen( mapReadyCallback: OnMapReadyCallback, ) { Box( modifier = Modifier .fillMaxWidth() .fillMaxHeight(), ) { MapView(mapReadyCallback) } } @Composable fun MapView(mapReadyCallback: OnMapReadyCallback) { AndroidView( factory = { context -> val mapView = org.maplibre.android.maps.MapView(context) mapView.onCreate(null) mapView.getMapAsync(mapReadyCallback) mapView }, ) }
  3. 編寫代碼以顯示地圖。

    1. 將以下代碼添加到您的MainActivity.kt文件中。

      // ...other imports import org.maplibre.android.MapLibre import org.maplibre.android.camera.CameraPosition import org.maplibre.android.geometry.LatLng import org.maplibre.android.maps.MapLibreMap import org.maplibre.android.maps.OnMapReadyCallback import org.maplibre.android.maps.Style class MainActivity : ComponentActivity(), OnMapReadyCallback { private val region = "YOUR_AWS_REGION" private val mapName = "YOUR_AWS_MAP_NAME" private val apiKey = "YOUR_AWS_API_KEY" override fun onCreate(savedInstanceState: Bundle?) { MapLibre.getInstance(this) super.onCreate(savedInstanceState) setContent { TestMapAppTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { MapLoadScreen(this) } } } } override fun onMapReady(map: MapLibreMap) { map.setStyle( Style.Builder() .fromUri( "https://maps.geo.$region.amazonaws.com/maps/v0/maps/$mapName/style-descriptor?key=$apiKey" ), ) { map.uiSettings.isAttributionEnabled = true map.uiSettings.isLogoEnabled = false map.uiSettings.attributionGravity = Gravity.BOTTOM or Gravity.END val initialPosition = LatLng(47.6160281982247, -122.32642111977668) map.cameraPosition = CameraPosition.Builder() .target(initialPosition) .zoom(14.0) .build() } } }
    2. 儲存 MainActivity.kt 檔案。您現在可以建置應用程式。要運行它,您可能必須設置一個設備來模擬它 AndroidStudio,或在設備上使用該應用程序。使用此應用程序查看地圖控件的行為。您可以通過在地圖上拖動並捏住以進行縮放來進行平移。

      在下一節中,您將在地圖上添加一個標記,並在移動地圖時顯示標記所在位置的地址。

將反向地理編碼搜尋新增至應用程式

現在,您將向應用程序添加反向地理編碼搜索,您可以在其中找到某個位置的項目。為了簡化使用 Android 應用程序,我們將搜索屏幕的中心。若要尋找新位置,請將地圖移至您要搜尋的位置。我們將在地圖的中心放置一個標記,以顯示我們正在搜索的位置。

新增反向地理編碼搜尋將由兩部分組成。

  • 在屏幕的中心添加一個標記,以向用戶顯示我們正在搜索的位置。

  • 為結果添加文本框,然後搜索標記位置的內容並在文本框中顯示它。

若要將標記新增至您的應用程式
  1. 將此圖像保存到app/res/drawable文件夾中的項目中red_marker.png(您也可以從中訪問圖像GitHub。或者,您也可以建立映像檔。您也可以對不想顯示的零件使用具有透明度的 .png 檔案。

  2. 將以下代碼添加到您的 MapLoadScreen.kt 文件中。

    // ...other imports import androidx.compose.foundation.Image import androidx.compose.foundation.layout.size import androidx.compose.ui.Alignment import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.dp import com.amazon.testmapapp.R @Composable fun MapLoadScreen( mapReadyCallback: OnMapReadyCallback, ) { Box( modifier = Modifier .fillMaxWidth() .fillMaxHeight(), ) { MapView(mapReadyCallback) Box( modifier = Modifier .align(Alignment.Center), ) { Image( painter = painterResource(id = R.drawable.red_marker), contentDescription = "marker", modifier = Modifier .size(40.dp) .align(Alignment.Center), ) } } } @Composable fun MapView(mapReadyCallback: OnMapReadyCallback) { AndroidView( factory = { context -> val mapView = org.maplibre.android.maps.MapView(context) mapView.onCreate(null) mapView.getMapAsync(mapReadyCallback) mapView }, ) }
  3. 構建並運行您的應用程序以預覽功能。

您的應用程序現在在屏幕上有一個標記。在這種情況下,它是不會移動的靜態圖像。它用於顯示地圖視圖的中心,這是我們將搜索的地方。在以下過程中,我們將在該位置添加搜索。

將某個位置的反向地理編碼搜尋新增至您的應用程式
  1. 在「項目」窗口中,在樹視圖中打開 gradlelibs.versions.toml文件。這將打開libs.versions.toml文件進行編輯。現在在libs.versions.toml文件中添加以下版本和庫數據。

    [versions] ... okhttp = "4.12.0" [libraries] ... com-squareup-okhttp3 = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" } [plugins] ...
  2. 完成編輯libs.versions.toml檔案後, AndroidStudio 必須重新同步專案。在編libs.versions.toml輯視窗頂端, AndroidStudio 會提示您同步。選擇「立即同步」以同步您的項目,然後再繼續。

  3. 在「項目」窗口中,在樹視圖中打開 Gradle 腳本,然後選擇應用程序模塊的build.gradle文件。這將打開build.gradle文件進行編輯。

  4. 在檔案底部的 [相依性] 區段中,新增下列相依性。

    dependencies { ... implementation(libs.com.squareup.okhttp3) }
  5. 完成編輯 Gradle 依賴關係後, AndroidStudio 必須重新同步項目。在編build.gradle輯視窗頂端, AndroidStudio 會提示您同步。選取SyncNow以同步您的專案,然後再繼續。

  6. 現在,在樹視圖中將數據添加到請求目錄中,並創建ReverseGeocodeRequest.kt數據類。將以下代碼添加到類中。

    import com.google.gson.annotations.SerializedName data class ReverseGeocodeRequest( @SerializedName("Language") val language: String, @SerializedName("MaxResults") val maxResults: Int, @SerializedName("Position") val position: ListDouble )
  7. 現在,在樹視圖中將數添加到響應目錄中,並創建ReverseGeocodeResponse.kt數據類。在其中添加以下代碼。

    import com.google.gson.annotations.SerializedName data class ReverseGeocodeResponse( @SerializedName("Results") val results: ListResult ) data class Result( @SerializedName("Place") val place: Place ) data class Place( @SerializedName("Label") val label: String )
  8. 現在,從項目窗口,打開應用程序,Java,你的包名在樹視圖,並轉到 ui 文件夾,裡面 ui 文件夾創建視圖模型目錄。

  9. 裡面視圖模型目錄創建MainViewModel.kt文件。

  10. 將以下代碼添加到您的MainViewModel.kt文件中。

    import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import com.amazon.testmapapp.data.request.ReverseGeocodeRequest import com.amazon.testmapapp.data.response.ReverseGeocodeResponse import com.google.gson.Gson import java.io.IOException import okhttp3.Call import okhttp3.Callback import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.Response import org.maplibre.android.geometry.LatLng import org.maplibre.android.maps.MapLibreMap class MainViewModel : ViewModel() { var label by mutableStateOf("") var isLabelAdded: Boolean by mutableStateOf(false) var client = OkHttpClient() var mapLibreMap: MapLibreMap? = null fun reverseGeocode(latLng: LatLng, apiKey: String) { val region = "YOUR_AWS_REGION" val indexName = "YOUR_AWS_PLACE_INDEX" val url = "https://places.geo.${region}.amazonaws.com/places/v0/indexes/${indexName}/search/position?key=${apiKey}" val requestBody = ReverseGeocodeRequest( language = "en", maxResults = 1, position = listOf(latLng.longitude, latLng.latitude) ) val json = Gson().toJson(requestBody) val mediaType = "application/json".toMediaTypeOrNull() val request = Request.Builder() .url(url) .post(json.toRequestBody(mediaType)) .build() client.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { e.printStackTrace() } override fun onResponse(call: Call, response: Response) { if (response.isSuccessful) { val jsonResponse = response.body?.string() val reverseGeocodeResponse = Gson().fromJson(jsonResponse, ReverseGeocodeResponse::class.java) val responseLabel = reverseGeocodeResponse.results.firstOrNull()?.place?.label if (responseLabel != null) { label = responseLabel isLabelAdded = true } } } }) } }
  11. 如果尚未開啟,請開啟MapLoadScreen.kt檔案,如先前程序所示。新增以下程式碼。這將創建一個撰寫文本視圖,您將在其中看到位置的反向地理編碼搜索結果。

    // ...other imports import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.Text import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.testTag import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.sp import com.amazon.testmapapp.ui.viewModel.MainViewModel @Composable fun MapLoadScreen( mapReadyCallback: OnMapReadyCallback, mainViewModel: MainViewModel, ) { Box( modifier = Modifier .fillMaxWidth() .fillMaxHeight(), ) { MapView(mapReadyCallback) Box( modifier = Modifier .align(Alignment.Center), ) { Image( painter = painterResource(id = R.drawable.red_marker), contentDescription = "marker", modifier = Modifier .size(40.dp) .align(Alignment.Center), ) } if (mainViewModel.isLabelAdded) { Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Bottom ) { Box( modifier = Modifier .fillMaxWidth() .background(Color.White), ) { Text( text = mainViewModel.label, modifier = Modifier .padding(16.dp) .align(Alignment.Center) .testTag("label") .semantics { contentDescription = "label" }, fontSize = 14.sp, ) } Spacer(modifier = Modifier.height(80.dp)) } } } } @Composable fun MapView(mapReadyCallback: OnMapReadyCallback) { AndroidView( factory = { context -> val mapView = org.maplibre.android.maps.MapView(context) mapView.onCreate(null) mapView.getMapAsync(mapReadyCallback) mapView }, ) }
  12. 在應用程式中的 java 下的套件名稱資料夾中 AndroidStudio,開啟MainActivity.kt檔案。如圖所示修改代碼。

    // ...other imports import androidx.activity.viewModels import com.amazon.testmapapp.ui.viewModel.MainViewModel class MainActivity : ComponentActivity(), OnMapReadyCallback, MapLibreMap.OnCameraMoveStartedListener, MapLibreMap.OnCameraIdleListener { private val mainViewModel: MainViewModel by viewModels() private val region = "YOUR_AWS_REGION" private val mapName = "YOUR_AWS_MAP_NAME" private val apiKey = "YOUR_AWS_API_KEY" override fun onCreate(savedInstanceState: Bundle?) { MapLibre.getInstance(this) super.onCreate(savedInstanceState) setContent { TestMapAppTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { MapLoadScreen(this, mainViewModel) } } } } override fun onMapReady(map: MapLibreMap) { map.setStyle( Style.Builder() .fromUri( "https://maps.geo.$region.amazonaws.com/maps/v0/maps/$mapName/style-descriptor?key=$apiKey" ), ) { map.uiSettings.isAttributionEnabled = true map.uiSettings.isLogoEnabled = false map.uiSettings.attributionGravity = Gravity.BOTTOM or Gravity.END val initialPosition = LatLng(47.6160281982247, -122.32642111977668) map.cameraPosition = CameraPosition.Builder() .target(initialPosition) .zoom(14.0) .build() map.addOnCameraMoveStartedListener(this) map.addOnCameraIdleListener(this) map.cameraPosition.target?.let { latLng -> mainViewModel.reverseGeocode( LatLng( latLng.latitude, latLng.longitude ), apiKey ) } } } override fun onCameraMoveStarted(p0: Int) { mainViewModel.label = "" mainViewModel.isLabelAdded = false } override fun onCameraIdle() { if (!mainViewModel.isLabelAdded) { mainViewModel.mapLibreMap?.cameraPosition?.target?.let { latLng -> mainViewModel.reverseGeocode( LatLng( latLng.latitude, latLng.longitude ), apiKey ) } } } }

    此代碼適用於地圖視圖。中的虛擬相機位置定義了地圖視圖 MapLibre。移動地圖可以被認為是移動該虛擬攝像機。

    • ViewModel 具有標籤變數:此變數會在撰寫文字檢視中設定資料。

    • onMapReady:此功能已更新以註冊兩個新事件。

    • 每當用戶移動地圖時都會發生該onCameraMove事件。一般來說,移動地圖時,我們要隱藏搜索,直到用戶完成移動地圖。

    • 當用戶暫停移動地圖發生該onCameraIdle事件。此事件會呼叫我們的反向地理編碼功能,在地圖中央進行搜尋。

    • reverseGeocode(latLng: LatLng, apiKey: String):此功能(在事件 onCameraIdle中調用)在地圖的中心搜索位置並更新標籤以顯示結果。它使用相機目標,該目標定義了地圖的中心(相機正在查看的位置)。

  13. 保存您的文件,並構建和運行您的應用程序,以查看其是否有效。

具有搜索功能的快速啟動應用程序已完成。

新增追蹤至您的應用程式

若要將追蹤新增至範例應用程式,請依照下列步驟執行:

  1. 將跟踪和身份驗證 SDK 依賴項添加到您的項目中。

  2. 在 AndroidManifest .xml 檔案中包含權限和服務項目。

  3. 使用撰寫設定開始/停止追蹤按鈕程式碼。

  4. 添加用於創建 LocationTracker 對象的代碼,並開始和停止跟踪。

  5. 使用安卓模擬器創建一個測試路線。

  1. 將跟踪和身份驗證 SDK 依賴項添加到您的項目中。

    1. 在「項目」窗口中,打開 gradle,然後在樹視圖中打開libs.versions.toml文件。這將打開libs.versions.toml文件進行編輯。現在在libs.versions.toml文件中添加以下版本和庫數據。

      [versions] ... auth = "0.0.1" tracking = "0.0.1" [libraries] ... auth = { group = "software.amazon.location", name = "auth", version.ref = "auth" } tracking = { module = "software.amazon.location:tracking", version.ref = "tracking" } [plugins] ...
    2. 完成編輯libs.versions.toml檔案後, AndroidStudio 必須重新同步專案。在編libs.versions.toml輯視窗頂端, AndroidStudio 會提示您同步。選擇「立即同步」以同步您的項目,然後再繼續。

    3. 在「項目」窗口中,在樹視圖中打開 Gradle 腳本,然後選擇應用程序模塊的build.gradle文件。這將打開build.gradle文件進行編輯。

    4. 在檔案底部的 [相依性] 區段中,新增下列相依性。

      dependencies { ... implementation(libs.auth) implementation(libs.tracking) }
    5. 完成編輯 Gradle 依賴關係後, AndroidStudio 必須重新同步項目。在構建 .gradle 編輯窗口的頂部, AndroidStudio 提示您同步。選取SyncNow以同步您的專案,然後再繼續。

  2. 在 AndroidManifest .xml 檔案中包含權限和服務項目。

    1. 若要在您的中包含正確的權限和服務項目AndroidManifest.xml file,請使用下列程式碼更新檔案:

      <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.INTERNET"/> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.AndroidQuickStartApp" tools:targetApi="31"> <activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name" android:theme="@style/Theme.AndroidQuickStartApp"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
  3. 使用撰寫設定開始/停止追蹤按鈕程式碼。

    1. 添加兩個圖像播放和暫停在分辨率可繪製命名為 ic_pause 和 ic_ play。您也可以從中存取影像GitHub

    2. 如果尚未開啟,請開啟MapLoadScreen.kt檔案,如先前程序所示。新增以下程式碼。這將創建一個撰寫按鈕視圖,我們可以點擊它開停止跟踪。

      // ...other imports import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults @Composable fun MapLoadScreen( mapReadyCallback: OnMapReadyCallback, mainViewModel: MainViewModel, onStartStopTrackingClick: () -> Unit ) { Box( modifier = Modifier .fillMaxWidth() .fillMaxHeight(), ) { MapView(mapReadyCallback) Box( modifier = Modifier .align(Alignment.Center), ) { Image( painter = painterResource(id = R.drawable.red_marker), contentDescription = "marker", modifier = Modifier .size(40.dp) .align(Alignment.Center), ) } if (mainViewModel.isLabelAdded) { Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.Bottom ) { Box( modifier = Modifier .fillMaxWidth() .background(Color.White), ) { Text( text = mainViewModel.label, modifier = Modifier .padding(16.dp) .align(Alignment.Center) .testTag("label") .semantics { contentDescription = "label" }, fontSize = 14.sp, ) } Spacer(modifier = Modifier.height(80.dp)) } } Column( modifier = Modifier .fillMaxSize() .padding(bottom = 16.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Bottom, ) { Button( onClick = onStartStopTrackingClick, modifier = Modifier .padding(horizontal = 16.dp) ) { Text( text = if (mainViewModel.isLocationTrackingForegroundActive) "Stop tracking" else "Start tracking", color = Color.Black ) Spacer(modifier = Modifier.size(ButtonDefaults.IconSpacing)) Image( painter = painterResource(id = if (mainViewModel.isLocationTrackingForegroundActive) R.drawable.ic_pause else R.drawable.ic_play), contentDescription = if (mainViewModel.isLocationTrackingForegroundActive) "stop_tracking" else "start_tracking" ) } } } } @Composable fun MapView(mapReadyCallback: OnMapReadyCallback) { AndroidView( factory = { context -> val mapView = org.maplibre.android.maps.MapView(context) mapView.onCreate(null) mapView.getMapAsync(mapReadyCallback) mapView }, ) }
  4. 添加用於創建 LocationTracker 對象的代碼,並開始和停止跟踪。

    1. 在文件中添加以下代MainViewModel.kt碼。

      ... var isLocationTrackingForegroundActive: Boolean by mutableStateOf(false) var locationTracker: LocationTracker? = null
    2. 將以下代碼添加到您的MainActivity.kt文件中。

      // ...other imports import software.amazon.location.auth.AuthHelper import software.amazon.location.auth.LocationCredentialsProvider import software.amazon.location.tracking.LocationTracker import software.amazon.location.tracking.aws.LocationTrackingCallback import software.amazon.location.tracking.config.LocationTrackerConfig import software.amazon.location.tracking.database.LocationEntry import software.amazon.location.tracking.filters.DistanceLocationFilter import software.amazon.location.tracking.filters.TimeLocationFilter import software.amazon.location.tracking.util.TrackingSdkLogLevel class MainActivity : ComponentActivity(), OnMapReadyCallback, MapLibreMap.OnCameraMoveStartedListener, MapLibreMap.OnCameraIdleListener { private val mainViewModel: MainViewModel by viewModels() private val poolId = "YOUR_AWS_IDENTITY_POOL_ID" private val trackerName = "YOUR_AWS_TRACKER_NAME" private val region = "YOUR_AWS_REGION" private val mapName = "YOUR_AWS_MAP_NAME" private val apiKey = "YOUR_AWS_API_KEY" private val coroutineScope = MainScope() private lateinit var locationCredentialsProvider: LocationCredentialsProvider private lateinit var authHelper: AuthHelper override fun onCreate(savedInstanceState: Bundle?) { MapLibre.getInstance(this) super.onCreate(savedInstanceState) setContent { TestMapAppTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { MapLoadScreen(this, mainViewModel, onStartStopTrackingClick = { if (mainViewModel.isLocationTrackingForegroundActive) { mainViewModel.isLocationTrackingForegroundActive = false mainViewModel.locationTracker?.stop() } else { if (checkLocationPermission(this)) return@MapLoadScreen mainViewModel.isLocationTrackingForegroundActive = true mainViewModel.locationTracker?.start(locationTrackingCallback = object : LocationTrackingCallback { override fun onLocationAvailabilityChanged(locationAvailable: Boolean) { } override fun onLocationReceived(location: LocationEntry) { } override fun onUploadSkipped(entries: LocationEntry) { } override fun onUploadStarted(entries: ListLocationEntry) { } override fun onUploaded(entries: ListLocationEntry) { } }) } }) } } } authenticateUser() } private fun authenticateUser() { coroutineScope.launch { authHelper = AuthHelper(applicationContext) locationCredentialsProvider = authHelper.authenticateWithCognitoIdentityPool( poolId, ) locationCredentialsProvider.let { val config = LocationTrackerConfig( trackerName = trackerName, logLevel = TrackingSdkLogLevel.DEBUG, latency = 1000, frequency = 5000, waitForAccurateLocation = false, minUpdateIntervalMillis = 5000, ) mainViewModel.locationTracker = LocationTracker( applicationContext, it, config, ) mainViewModel.locationTracker?.enableFilter(TimeLocationFilter()) mainViewModel.locationTracker?.enableFilter(DistanceLocationFilter()) } } } private fun checkLocationPermission(context: Context) = ActivityCompat.checkSelfPermission( context, Manifest.permission.ACCESS_FINE_LOCATION, ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission( context, Manifest.permission.ACCESS_COARSE_LOCATION, ) != PackageManager.PERMISSION_GRANTED override fun onMapReady(map: MapLibreMap) { map.setStyle( Style.Builder() .fromUri( "https://maps.geo.$region.amazonaws.com/maps/v0/maps/$mapName/style-descriptor?key=$apiKey" ), ) { mainViewModel.mapLibreMap = map map.uiSettings.isAttributionEnabled = true map.uiSettings.isLogoEnabled = false map.uiSettings.attributionGravity = Gravity.BOTTOM or Gravity.END val initialPosition = LatLng(47.6160281982247, -122.32642111977668) map.cameraPosition = CameraPosition.Builder() .target(initialPosition) .zoom(14.0) .build() map.addOnCameraMoveStartedListener(this) map.addOnCameraIdleListener(this) map.cameraPosition.target?.let { latLng -> mainViewModel.reverseGeocode( LatLng( latLng.latitude, latLng.longitude ), apiKey ) } } } override fun onCameraMoveStarted(p0: Int) { mainViewModel.label = "" mainViewModel.isLabelAdded = false } override fun onCameraIdle() { if (!mainViewModel.isLabelAdded) { mainViewModel.mapLibreMap?.cameraPosition?.target?.let { latLng -> mainViewModel.reverseGeocode( LatLng( latLng.latitude, latLng.longitude ), apiKey ) } } } }

      上面的代碼演示了如何創建一個LocationTracker對象,以AuthHelper及如何啟動和停止跟踪 LocationTracker。

      • authenticateUser():此方法創建 AuthHelper 和 LocationTracker 對象。

      • onStartStopTrackingClick:當用戶單擊開始/停止跟踪按鈕時觸發此回調,該按鈕將使用 Tracking SDK 開始/停止跟踪。

  5. 使用安卓模擬器創建測試路線。

    1. 通過使用安卓工作室啟動 AVD 打開模擬器

    2. 通過單擊模擬器工具欄中的更多(三點)圖標打開擴展控件

    3. 從側邊欄中選擇「位置」以打開「位置」。

    4. 使用 GPX 數據創建路線,或通過單擊地圖並選擇源和目標數據來創建路線。

    5. 通過單擊播放路線始模擬開始模擬 GPS 路線。

    6. 通過運行您的應用程序並觀察它如何處理模擬路由來測試應用程序。

這是 Android 快速入門應用程序的完整演示。

下一步是什麼

此應用程式的原始程式碼可在上取得GitHub

要充分利用 Amazon 位置,您可以查看以下資源: