本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
創建一個安卓應用
在本節中,您將創建一個帶有地圖的 Android 應用程序,可以在一個位置進行搜索並在前台跟踪。首先,您將為您的應用程式建立您的 Amazon 位置資源、Amazon Cognito 身分和 API 金鑰。
為您的應用程式建立 Amazon 位置資源
如果您還沒有這些資源,則必須建立應用程式將使用的 Amazon 位置資源。在這裡,您可以創建一個地圖資源以在應用程序中顯示地圖,用於在地圖上搜索位置的位置索引,以及用於在地圖上跟踪對象的跟踪器。
將位置資源新增至您的應用程式
-
選擇您要使用的地圖樣式。
-
在 Amazon 位置主控台的「地圖
」頁面上,選擇「建立地圖」以預覽地圖樣式。 -
為新地圖資源新增「名稱」和「描述」。記下您用於地圖資源的名稱。稍後在自學課程中建立腳本檔案時,您將需要它。
-
我們建議您為您的地圖選擇 HERE 地圖。
注意
選擇地圖型式也會選擇您將使用的地圖資料提供者。如果您的應用程式正在追蹤或路由您在業務中使用的資產,例如送貨車輛或員工,您只能使用 HERE 作為您的地理位置供應商。如需詳細資訊,請參閱AWS 服務條款
第 82 節。 -
同意 Amazon 位置條款與條件,然後選擇「建立地圖」。您可以與選擇的地圖互動:放大、縮小或向任何方向平移。
-
記下為您的新地圖資源顯示的 Amazon 資源名稱(ARN)。您將在本教程稍後使用它來創建正確的身份驗證。
-
-
選擇您要使用的位置索引。
-
在「放置索引
」頁面上的 Amazon 位置主控台中,選擇「建立位置索引」。 -
新增位置索引資源的「名稱」和「說明」。記下您用於放置索引資源的名稱。稍後在自學課程中建立腳本檔案時,您將需要它。
-
選擇資料提供者。
注意
在大多數情況下,請選擇與您已選擇的地圖提供者相符的資料提供者。這有助於確保搜索將匹配地圖。
如果您的應用程式正在追蹤或路由您在業務中使用的資產,例如送貨車輛或員工,您只能使用 HERE 作為您的地理位置供應商。如需詳細資訊,請參閱AWS 服務條款
第 82 節。 -
選擇數據存儲選項。在本自學課程中,不會儲存結果,因此您可以選擇「否,僅限單次使用」。
-
同意 Amazon 地點條款與條件,然後選擇建立地點索引。
-
記下針對新位置索引資源顯示的 ARN。您將在本教程的下一部分中使用它來創建正確的身份驗證。
-
使用 Amazon 位置控制台創建跟踪器。
在左側導覽窗格中,選擇「追蹤器」。
選擇建立追蹤器。
填寫所有必填欄位。
在「位置篩選」下,我們建議您使用預設設定:TimeBased。
選擇創建跟踪器以完成。
為您的應用程式設定驗證
您在本教程中創建的應用程序具有匿名用法,這意味著您的用戶不需 AWS 要登錄即可使用該應用程序。但是,Amazon 定 Location Service API 需要身份驗證才能使用。您可以使用 API 金鑰或 Amazon Cognito 為匿名使用者提供身份驗證和授權。本教學將使用 Amazon Cognito 和 API 金鑰來驗證您的應用程式。
注意
如需將 Amazon Cognito 或 API 金鑰與 Amazon 定 Location Service 搭配使用的詳細資訊,請參閱授予對 Amazon 定 Location Service 的訪問。
以下教學課程說明如何設定地圖、位置索引和您在其中建立的追蹤器的身份驗證,以及如何設定 Amazon 位置的許可。
設定驗證
導覽至 Amazon 位置主控台
,然後從左側功能表中選取 API 金鑰。 點擊「創建 API 密鑰」。請記住,API 金鑰必須與先前建立的 Amazon 定位服務資源位於相同的 AWS 帳戶和區域。
在「創建 API 密鑰」頁面上填寫所需的詳細信息:
名稱:提供 API 金鑰的名稱,例如
MyAppKey
。資源:選擇先前建立的 Amazon 定 Location Service 地圖和放置索引資源。您可以通過選擇「添加資源」添加多個資源。這允許 API 密鑰與指定的資源一起使用。
動作:指定此 API 金鑰的授權動作。至少選取
geo:GetMap
和geo:SearchPlaceIndexForPosition
以確保自學課程如預期般運作。選用您可以新增說明、到期時間、標籤或參照者,例如
https://www.example.com
,將金鑰的使用量限制在特定網域,讓教學課程只能在該網域內運作。
按一下「建立 API 金鑰」以產生 API 金鑰。
選取 [顯示 API 金鑰] 並複製金鑰值,以
v1.public.a1b2c3d4
供稍後在教學課程中使用。
建立 IAM 政策以進行追蹤
以具有管理員許可的使用者身分登入 IAM 主控台 (https://console.aws.amazon.com/iam/)。
在導覽窗格上選擇 Policies (政策)。
在內容窗格中,選擇 Create policy (建立政策)。
-
選擇 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}" ] } ] }
這是追蹤的政策範例。若要將範例用於您自己的原則,請取代
Region
Account
、和TrackerName預留位置。注意
雖然未驗證身分集區的目的是在不安全的網際網路網站上曝光,但請注意,這些身分集區會被交換成標準且有時間限制的 AWS 登入資料。
適當範圍與未驗證身分集區相關聯的 IAM 角色非常重要。如需有關在 Amazon Cognito 與 Amazon 定 Location Service 務搭配使用和適當設定政策範圍的詳細資訊,請參閱授與 Amazon 定 Location Service 的存取權。
在「檢閱並建立」頁面上,輸入原則名稱欄位的名稱。檢閱原則所授與的權限,然後選擇 [建立原則] 儲存工作。
新的政策會出現在受管政策清單中,並且已準備好連接。
設定追蹤的驗證
開啟 [識別集區] 頁面。
注意
您建立的集 AWS 區必須與您在上一節中建立的 Amazon 定 Location Service 資源位於相同的 AWS 帳戶和區域。
選擇 [建立識別集區]。
從 [設定識別集區信任] 步驟開始。對於使用者存取驗證,請選取來賓存取,然後按下一步。
在 [設定權限] 頁面上,選取 [使用現有的 IAM 角色],然後輸入您在上一步中建立的 IAM 角色名稱。準備就緒後,按下一步繼續進行下一步。
在 [設定內容] 頁面上,提供身分識別集區的名稱。然後按下一頁.
在 [檢閱並建立] 頁面上,檢閱存在的所有資訊,然後按 [建立身分集區]。
開啟 [身分識別集區] 頁面,然後選取您剛建立的身分集區。然後複製或寫下 IdentityPoolId 您稍後將在瀏覽器腳本中使用的內容。
創建基本的安卓應用程序
在本教程中,您將創建一個嵌入地圖的 Android 應用程序,並允許用戶查找地圖上的位置。
首先,使用 Android 工作室的新項目嚮導創建一個空的 Kotlin 應用程序。
若要建立空白應用程式 (AndroidStudio)
開始 AndroidStudio。打開菜單,然後選擇文件,新建,新建項目。
從 [電話和平板電腦] 索引標籤中,選取 [空白活動],然後選擇 [下一步
為您的應用程式選擇「名稱」、「Pack age 名稱」和「儲存位置」。
在語言的下拉列表中,選擇 Kotlin。
選擇「完成」以建立空白應用程式。
將交互式地圖添加到您的應用程序
現在您已經建立了基本的應用程式,您可以將地圖控制項新增至您的應用程式。本教學課程使用 API 金鑰來管理地圖檢視。地圖控件本身是本MapLibre 機庫
要將映射添加到您的應用程序,您必須執行以下操作:
將 MapLibre 依賴項添加到您的項目中。
使用撰寫設定地圖檢視程式碼。
編寫代碼以顯示地圖。
使用以下步驟將地圖添加到您的應用程序:
將 MapLibre 依賴項添加到您的項目
在中 AndroidStudio,選取「檢視」功能表,然後選擇「工具視窗」、「專案」。這將打開項目窗口,該窗口使您可以訪問項目中的所有文件。
在「項目」窗口中,打開 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] ...
完成編輯
libs.versions.toml
檔案後, AndroidStudio 必須重新同步專案。在編libs.versions.toml
輯視窗頂端, AndroidStudio 會提示您同步。選擇「立即同步」以同步您的項目,然後再繼續。在「項目」窗口中,在樹視圖中打開 Gradle 腳本,然後選擇應用程序模塊的
build.gradle
文件。這將打開build.gradle
文件進行編輯。在檔案底部的 [相依性] 區段中,新增下列相依性。
dependencies { ... implementation(libs.org.maplibre.gl) }
完成添加搖籃依賴關係後,Android 工作室必須重新同步該項目。在編輯窗口的頂部,Android Studio 中,選擇立即同步以同步您的項目,然後再繼續。
現在,您將使用撰寫設置地圖視圖代碼。使用下列步驟:
從項目窗口中,在樹視圖中打開 App,Java,
您的包名稱
,然後轉到 ui 文件夾,在 ui 文件夾中創建一個視圖目錄。裡面的視圖目錄創建一個
MapLoadScreen.kt
文件。將以下代碼添加到您的
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 }, ) }
編寫代碼以顯示地圖。
將以下代碼添加到您的
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() } } }儲存
MainActivity.kt
檔案。您現在可以建置應用程式。要運行它,您可能必須設置一個設備來模擬它 AndroidStudio,或在設備上使用該應用程序。使用此應用程序查看地圖控件的行為。您可以通過在地圖上拖動並捏住以進行縮放來進行平移。在下一節中,您將在地圖上添加一個標記,並在移動地圖時顯示標記所在位置的地址。
將反向地理編碼搜尋新增至應用程式
現在,您將向應用程序添加反向地理編碼搜索,您可以在其中找到某個位置的項目。為了簡化使用 Android 應用程序,我們將搜索屏幕的中心。若要尋找新位置,請將地圖移至您要搜尋的位置。我們將在地圖的中心放置一個標記,以顯示我們正在搜索的位置。
新增反向地理編碼搜尋將由兩部分組成。
在屏幕的中心添加一個標記,以向用戶顯示我們正在搜索的位置。
為結果添加文本框,然後搜索標記位置的內容並在文本框中顯示它。
若要將標記新增至您的應用程式
將此圖像保存到
app/res/drawable
文件夾中的項目中red_marker.png
(您也可以從中訪問圖像GitHub。或者,您也可以建立映像檔。您也可以對不想顯示的零件使用具有透明度的 .png 檔案。 將以下代碼添加到您的 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 }, ) }
構建並運行您的應用程序以預覽功能。
您的應用程序現在在屏幕上有一個標記。在這種情況下,它是不會移動的靜態圖像。它用於顯示地圖視圖的中心,這是我們將搜索的地方。在以下過程中,我們將在該位置添加搜索。
將某個位置的反向地理編碼搜尋新增至您的應用程式
在「項目」窗口中,在樹視圖中打開 gradle 到
libs.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] ...
完成編輯
libs.versions.toml
檔案後, AndroidStudio 必須重新同步專案。在編libs.versions.toml
輯視窗頂端, AndroidStudio 會提示您同步。選擇「立即同步」以同步您的項目,然後再繼續。在「項目」窗口中,在樹視圖中打開 Gradle 腳本,然後選擇應用程序模塊的
build.gradle
文件。這將打開build.gradle
文件進行編輯。在檔案底部的 [相依性] 區段中,新增下列相依性。
dependencies { ... implementation(libs.com.squareup.okhttp3) }
完成編輯 Gradle 依賴關係後, AndroidStudio 必須重新同步項目。在編
build.gradle
輯視窗頂端, AndroidStudio 會提示您同步。選取SyncNow以同步您的專案,然後再繼續。現在,在樹視圖中將數據添加到請求目錄中,並創建
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: List
Double
)現在,在樹視圖中將數據添加到響應目錄中,並創建
ReverseGeocodeResponse.kt
數據類。在其中添加以下代碼。import com.google.gson.annotations.SerializedName data class ReverseGeocodeResponse( @SerializedName("Results") val results: List
Result
) data class Result( @SerializedName("Place") val place: Place ) data class Place( @SerializedName("Label") val label: String )現在,從項目窗口,打開應用程序,Java,
你的包名
在樹視圖,並轉到 ui 文件夾,裡面 ui 文件夾創建視圖模型目錄。裡面視圖模型目錄創建
MainViewModel.kt
文件。將以下代碼添加到您的
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 } } } }) } }如果尚未開啟,請開啟
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 }, ) }
在應用程式中的 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中調用)在地圖的中心搜索位置並更新標籤以顯示結果。它使用相機目標,該目標定義了地圖的中心(相機正在查看的位置)。
保存您的文件,並構建和運行您的應用程序,以查看其是否有效。
具有搜索功能的快速啟動應用程序已完成。
新增追蹤至您的應用程式
若要將追蹤新增至範例應用程式,請依照下列步驟執行:
將跟踪和身份驗證 SDK 依賴項添加到您的項目中。
在 AndroidManifest .xml 檔案中包含權限和服務項目。
使用撰寫設定開始/停止追蹤按鈕程式碼。
添加用於創建 LocationTracker 對象的代碼,並開始和停止跟踪。
使用安卓模擬器創建一個測試路線。
將跟踪和身份驗證 SDK 依賴項添加到您的項目中。
在「項目」窗口中,打開 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] ...
完成編輯
libs.versions.toml
檔案後, AndroidStudio 必須重新同步專案。在編libs.versions.toml
輯視窗頂端, AndroidStudio 會提示您同步。選擇「立即同步」以同步您的項目,然後再繼續。在「項目」窗口中,在樹視圖中打開 Gradle 腳本,然後選擇應用程序模塊的
build.gradle
文件。這將打開build.gradle
文件進行編輯。在檔案底部的 [相依性] 區段中,新增下列相依性。
dependencies { ... implementation(libs.auth) implementation(libs.tracking) }
完成編輯 Gradle 依賴關係後, AndroidStudio 必須重新同步項目。在構建 .gradle 編輯窗口的頂部, AndroidStudio 提示您同步。選取SyncNow以同步您的專案,然後再繼續。
在 AndroidManifest .xml 檔案中包含權限和服務項目。
若要在您的中包含正確的權限和服務項目
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>
使用撰寫設定開始/停止追蹤按鈕程式碼。
添加兩個圖像播放和暫停在分辨率下可繪製命名為 ic_pause 和 ic_ play。您也可以從中存取影像GitHub
。 如果尚未開啟,請開啟
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 }, ) }
添加用於創建 LocationTracker 對象的代碼,並開始和停止跟踪。
在文件中添加以下代
MainViewModel.kt
碼。... var isLocationTrackingForegroundActive: Boolean by mutableStateOf(false) var locationTracker: LocationTracker? = null
將以下代碼添加到您的
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 開始/停止跟踪。
使用安卓模擬器創建測試路線。
通過使用安卓工作室啟動 AVD 打開模擬器。
通過單擊模擬器工具欄中的更多(三點)圖標打開擴展控件。
從側邊欄中選擇「位置」以打開「位置」。
使用 GPX 數據創建路線,或通過單擊地圖並選擇源和目標數據來創建路線。
通過單擊播放路線開始模擬開始模擬 GPS 路線。
通過運行您的應用程序並觀察它如何處理模擬路由來測試應用程序。
這是 Android 快速入門應用程序的完整演示。
下一步是什麼
此應用程式的原始程式碼可在上取得GitHub
要充分利用 Amazon 位置,您可以查看以下資源: