paint-brush
Chuyển đổi bản đồ di động bằng Jetpack Compose: Thông tin chi tiết từ Google I/O dành cho nhà phát triểntừ tác giả@darrylbayliss
Bài viết mới

Chuyển đổi bản đồ di động bằng Jetpack Compose: Thông tin chi tiết từ Google I/O dành cho nhà phát triển

từ tác giả Darryl Bayliss22m2024/07/09
Read on Terminal Reader

dài quá đọc không nổi

Jetpack Compose cho Maps đã chuyển đổi hoạt động phát triển Android hiện đại, giúp việc lập bản đồ trên thiết bị di động trở nên dễ dàng và hiệu quả hơn. Khám phá các kỹ thuật và mẹo chính từ Google I/O Extended 2023 về cách tích hợp Google Maps vào các dự án Jetpack Compose của bạn.
featured image - Chuyển đổi bản đồ di động bằng Jetpack Compose: Thông tin chi tiết từ Google I/O dành cho nhà phát triển
Darryl Bayliss HackerNoon profile picture
0-item



Bài đăng này dựa trên bài nói chuyện tại Google I/O Extended dành cho Nhóm nhà phát triển Google ở Luân Đôn vào tháng 7 năm 2023. Các trang trình bày cho bài nói chuyện có tại đây

Jetpack Compose cho Maps

Thật khó để tưởng tượng Jetpack Compose 1.0 được phát hành vào tháng 7 năm 2021 . Hai năm trôi qua nhanh chóng và với 24% trong số 1000 ứng dụng hàng đầu trên Google Play sử dụng Compose, thật dễ hiểu lý do.



Trong số tất cả sự phấn khích, một góc của Phát triển Android hiện đại mà tôi cảm thấy ít được chú ý là Google Maps. Đã lâu rồi tôi không sử dụng SDK nên tôi rất ngạc nhiên khi thấy Google Maps đã bắt kịp thời đại và phát hành thư viện Compose của riêng mình.


Đây sẽ là tin tức đáng mừng đối với các công ty và kỹ sư đang làm việc trong lĩnh vực lập bản đồ. Lập bản đồ di động là một ngành trị giá 35,5 tỷ USD, với dự đoán nó sẽ tăng lên 87,7 tỷ USD vào năm 2028 với Tốc độ tăng trưởng gộp hàng năm (CAGR) là 19,83%. Nguồn


Biểu đồ thị trường tăng trưởng


Sao nó lại quan trọng? Thị trường lớn hơn có nghĩa là có nhiều cơ hội hơn cho các công ty có được doanh thu từ các ứng dụng bản đồ di động. Chúng bao gồm các trường hợp sử dụng thông thường, thực phẩm, giao hàng tạp hóa và dịch vụ gọi xe. Tuy nhiên, nếu bạn tìm hiểu sâu hơn, sẽ có những ứng dụng không rõ ràng ngay lập tức.


Dưới đây là những ví dụ tôi có thể tìm thấy sau khi tìm kiếm ngắn gọn:


Khu vực thị trường tăng trưởng Bản đồ di động rất phù hợp cho Thành phố thông minh, giúp quản lý nhịp tim của thành phố và trực quan hóa dữ liệu theo cách để hiểu rõ hơn và ứng phó với những thách thức của thành phố. Hữu ích cho các nhà quy hoạch thành phố, các tổ chức ứng phó khẩn cấp hoặc cư dân hàng ngày.


Quản lý tài nguyên cũng được hưởng lợi từ các giải pháp lập bản đồ. Từ nông nghiệp đến đánh cá, khai thác mỏ đến lâm nghiệp, bản đồ cung cấp cho những người làm công việc này một góc nhìn để đưa ra quyết định đúng đắn nhằm thu hoạch nguyên liệu bền vững.


Giao thông vận tải phụ thuộc rất nhiều vào công nghệ bản đồ. Không chỉ các ứng dụng dành cho người tiêu dùng như Google Maps hay Uber, mà còn có các chức năng cấp doanh nghiệp như hiểu vị trí của đội xe của doanh nghiệp. Các cơ quan vận tải cũng sử dụng bản đồ để quản lý giao thông và giúp đưa ra quyết định về nơi điều hướng giao thông để giảm bớt lưu lượng.


Cuối cùng, với tình trạng biến đổi khí hậu và thời tiết ngày càng khó dự đoán, bản đồ cho phép các cơ quan khí tượng, đơn vị ứng phó khẩn cấp và nhà bảo tồn động vật hoang dã hiểu được thế giới của chúng ta đang thay đổi như thế nào và chúng ta có thể làm gì để thực hiện các bước tích cực nhằm giảm thiểu điều này.

Nguồn:Mordor Intelligence , GMInsights , Allied Market Research , EMR Research , Google Earth Outreach , Nghiên cứu & Thị trường


Với việc thế giới cung cấp ngày càng nhiều dữ liệu, đây là thời điểm tốt để tìm hiểu cách đưa dữ liệu đó lên bản đồ. Hãy làm điều đó và quay trở lại với mã.


Sử dụng Google Maps cho Compose

Google Maps for Compose dựa vào các phần phụ thuộc sau:

 dependencies { implementation "com.google.maps.android:maps-compose:2.11.4" implementation "com.google.android.gms:play-services-maps:18.1.0" // Optional Util Library implementation "com.google.maps.android:maps-compose-utils:2.11.4" implementation 'com.google.maps.android:maps-compose-widgets:2.11.4' // Optional Accompanist permissions to request permissions in compose implementation "com.google.accompanist:accompanist-permissions:0.31.5-beta" }


Google Maps dành cho Compose được xây dựng dựa trên SDK Google Maps, vì vậy bạn cần nhập thư viện Compose và SDK Maps. Bạn sẽ không cần sử dụng hầu hết các đối tượng trong SDK Google Maps vì thư viện soạn thảo bao gồm hầu hết các đối tượng này trong Thành phần kết hợp.


Thư viện utils và widget là phần phụ thuộc tùy chọn. Thư viện utils cung cấp khả năng phân cụm các điểm đánh dấu trên bản đồ, trong khi các tiện ích cung cấp các thành phần giao diện người dùng bổ sung. Bạn sẽ thấy những thứ này được sử dụng sau này.


Đối với bài đăng này, tôi đã bao gồm thư viện yêu cầu quyền từ Người đồng hành để trình bày cách yêu cầu quyền truy cập vị trí, quyền thường được sử dụng với bản đồ. Accompanist là một thư viện thử nghiệm để Google dùng thử và thu thập phản hồi về các tính năng chưa có trong Jetpack Compose.


Cuối cùng, bạn cần truy cập Bảng điều khiển dành cho nhà phát triển của Google , đăng ký khóa API SDK Google Maps và thêm khóa đó vào dự án của bạn. Có hướng dẫn trên Tài liệu dành cho nhà phát triển Google Maps về cách thực hiện việc này.


Mẹo bảo mật: Trong Bảng điều khiển dành cho nhà phát triển của Google, hãy khóa khóa API để khóa chỉ hoạt động với ứng dụng của bạn. Điều này tránh mọi việc sử dụng trái phép.


Hiển thị bản đồ

Hiển thị bản đồ đơn giản như sau:


 setContent { val hydePark = LatLng(51.508610, -0.163611) val cameraPositionState = rememberCameraPositionState { position = CameraPosition.fromLatLngZoom(hydePark, 10f) } GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState) { Marker( state = MarkerState(position = hydePark), title = "Hyde Park", snippet = "Marker in Hyde Park" ) } }


Tạo một đối tượng LatLng với vị trí của một khu vực và sử dụng nó kết hợp với rememberCameraPositionState để đặt vị trí ban đầu của camera. Phương pháp này ghi nhớ vị trí của bản đồ khi bạn di chuyển bằng tay hoặc theo chương trình. Nếu không có phương pháp này, Compose sẽ tính toán lại bản đồ về vị trí ban đầu mỗi khi thay đổi trạng thái.


Tiếp theo, tạo bản soạn thảo GoogleMap và chuyển vào công cụ sửa đổi mà bạn chọn và trạng thái máy ảnh. GoogleMap cũng cung cấp API Slot để chuyển vào các thành phần kết hợp bổ sung, những thành phần kết hợp này là những gì bạn muốn vẽ trên bản đồ.


Thêm thành phần kết hợp Marker , sau đó thêm MarkerState chứa vị trí của điểm đánh dấu bên trong. Cuối cùng, thêm tiêu đề và mô tả về điểm đánh dấu.


Việc chạy chương trình này mang lại tầm nhìn tuyệt đẹp về phía Tây London từ trên cao với điểm đánh dấu ở Công viên Hyde.


Điểm đánh dấu ở công viên Hyde

Tùy chỉnh cửa sổ đánh dấu

Bạn có thể tùy chỉnh cửa sổ của điểm đánh dấu bằng cách sử dụng Thành phần kết hợp MarkerInfoWindowContent . Điều này cũng có API dựa trên vị trí, nghĩa là bạn có thể chuyển các thành phần kết hợp của mình để hiển thị giao diện người dùng tùy chỉnh trong cửa sổ.


 setContent { val hydePark = LatLng(51.508610, -0.163611) val cameraPositionState = rememberCameraPositionState { position = CameraPosition.fromLatLngZoom(hydePark, 10f) } GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState) { MarkerInfoWindowContent( state = MarkerState(position = hydePark), title = "Hyde Park", snippet = "Marker in Hyde Park" ) { marker -> Column(horizontalAlignment = Alignment.CenterHorizontally) { Text( modifier = Modifier.padding(top = 6.dp), text = marker.title ?: "", fontWeight = FontWeight.Bold ) Text("Hyde Park is a Grade I-listed parked in Westminster") Image( modifier = Modifier .padding(top = 6.dp) .border( BorderStroke(3.dp, color = Color.Gray), shape = RectangleShape ), painter = painterResource(id = R.drawable.hyde_park), contentDescription = "A picture of hyde park" ) } } } }


Việc chạy này sẽ hiển thị cửa sổ tùy chỉnh phía trên điểm đánh dấu khi bạn nhấn vào nó.


Điểm đánh dấu cửa sổ tùy chỉnh ở Hyde Park

Hiển thị nhiều điểm đánh dấu

Hiển thị nhiều điểm đánh dấu cũng đơn giản như việc thêm bao nhiêu tùy ý bạn. Hãy thêm điểm đánh dấu cho một số công viên khác nhau ở Tây London.


 setContent { val hydePark = LatLng(51.508610, -0.163611) val regentsPark = LatLng(51.531143, -0.159893) val primroseHill = LatLng(51.539556, -0.16076088) val cameraPositionState = rememberCameraPositionState { position = CameraPosition.fromLatLngZoom(hydePark, 10f) } GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState) { // Marker 1 Marker( state = MarkerState(position = hydePark), title = "Hyde Park", snippet = "Marker in Hyde Park" ) // Marker 2 Marker( state = MarkerState(position = regentsPark), title = "Regents Park", snippet = "Marker in Regents Park" ) // Marker 3 Marker( state = MarkerState(position = primroseHill), title = "Primrose Hill", snippet = "Marker in Primrose Hill" ) } }


Chạy mã và bạn sẽ thấy điểm đánh dấu của mình xuất hiện trên bản đồ.


Nhiều điểm đánh dấu công viên


Điểm đánh dấu phân cụm

Bản đồ có thể trở nên bận rộn trong một khoảng thời gian ngắn. Nếu bạn đang cố gắng hiển thị 300 điểm đánh dấu, người dùng sẽ khó hiểu được chuyện gì đang xảy ra. Google Maps và thiết bị của bạn cũng sẽ không cảm ơn bạn vì nó sẽ phải hiển thị từng điểm đánh dấu , ảnh hưởng đến hiệu suất và thời lượng pin.


Giải pháp cho vấn đề này là Clustering , một kỹ thuật nhóm các điểm đánh dấu gần nhau thành một điểm đánh dấu duy nhất. Việc phân cụm này diễn ra trên cơ sở mức thu phóng. Khi bạn thu nhỏ bản đồ, các điểm đánh dấu sẽ nhóm thành một cụm, khi bạn phóng to, cụm sẽ chia thành các điểm đánh dấu riêng lẻ.


Google Maps dành cho Compose cung cấp tính năng này ngay lập tức thông qua thành phần Clustering . Không cần phải viết cách sắp xếp hoặc lọc phức tạp để thực hiện phân cụm.


 setContent { val hydePark = LatLng(51.508610, -0.163611) val regentsPark = LatLng(51.531143, -0.159893) val primroseHill = LatLng(51.539556, -0.16076088) val crystalPalacePark = LatLng(51.42153, -0.05749) val greenwichPark = LatLng(51.476688, 0.000130) val lloydPark = LatLng(51.364188, -0.080703) val cameraPositionState = rememberCameraPositionState { position = CameraPosition.fromLatLngZoom(hydePark, 10f) } GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState) { val parkMarkers = remember { mutableStateListOf( ParkItem(hydepark, "Hyde Park", "Marker in hyde Park"), ParkItem(regentspark, "Regents Park", "Marker in Regents Park"), ParkItem(primroseHill, "Primrose Hill", "Marker in Primrose Hill"), ParkItem(crystalPalacePark, "Crystal Palace", "Marker in Crystal Palace"), ParkItem(greenwichPark, "Greenwich Park", "Marker in Greenwich Park"), ParkItem(lloydPark, "Lloyd park", "Marker in Lloyd Park"), ) } Clustering(items = parkMarkers, onClusterClick = { // Handle when the cluster is tapped }, onClusterItemClick = { marker -> // Handle when a marker in the cluster is tapped }) } } data class ParkItem( val itemPosition: LatLng, val itemTitle: String, val itemSnippet: String) : ClusterItem { override fun getPosition(): LatLng = itemPosition override fun getTitle(): String = itemTitle override fun getSnippet(): String = itemSnippet }


Lưu ý lớp dữ liệu ParkItem đã được thêm vào. Chúng tôi cần điều này vì các mục được chuyển vào thành phần kết hợp Clustering phải tuân theo giao diện ClusterItem . Giao diện cung cấp cho Cụm vị trí, tiêu đề và đoạn mã cho mỗi điểm đánh dấu.

Phóng to và thu nhỏ và bạn sẽ thấy hoạt động phân cụm.


Nhận quyền vị trí

Bản đồ và vị trí của người dùng thường đi đôi với nhau, do đó, việc một số ứng dụng bản đồ yêu cầu quyền đối với vị trí của người dùng là điều hợp lý.

Hãy tôn trọng quyền của người dùng nếu bạn làm điều này, quyền truy cập vị trí là một trong những quyền nhạy cảm nhất cần có được từ người dùng.


Đảm bảo bạn thông báo cho người dùng lý do bạn cần quyền này và tích cực thể hiện lợi ích của việc cấp quyền đó. Điểm thưởng nếu ứng dụng của bạn hoạt động một phần mà không cần sự cho phép.


Google cung cấp một số hướng dẫn hữu ích về cách xử lý vị trí của người dùng cũng như hướng dẫn riêng để truy cập dữ liệu vị trí ở chế độ nền .


Vì vậy, bạn đã thực hiện thẩm định và quyết định rằng bạn cần có sự cho phép của người dùng để truy cập vào vị trí. Với thư viện quyền trong Accompanist, bạn thực hiện như sau:


 // Don't forget to add the permissions to AndroidManifest.xml val allLocationPermissionState = rememberMultiplePermissionsState( listOf(android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION) ) // Check if we have location permissions if (!allLocationPermissionsState.allPermissionsGranted) { // Show a component to request permission from the user Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, modifier = Modifier .padding(horizontal = 36.dp) .clip(RoundedCornerShape(16.dp)) .background(Color.white) ) { Text( modifier = Modifier.padding(top = 6.dp), textAlign = TextAlign.Center, text = "This app functions 150% times better with percise location enabled" ) Button(modifier = Modifier.padding(top = 12.dp), onClick = { allLocationPermissionsState.launchMultiplePermissionsRequest() }) { Text(text = "Grant Permission") } } }


Thông qua người đi kèm, chúng tôi đang kiểm tra xem liệu ứng dụng có quyền truy cập vào quyền ACCESS_FINE_LOCATION hay mức độ chính xác cao của GPS bằng tiếng Anh hay không. Điều quan trọng là phải bao gồm các quyền được yêu cầu trong tệp kê khai Android vì bạn sẽ không thể yêu cầu các quyền đó bằng cách khác.


Hệ thống Android và cửa hàng Google Play cũng sử dụng tệp kê khai để hiểu cách ứng dụng của bạn hoạt động và thông báo cho người dùng.

Nếu quyền không được cấp, một hộp thoại nhỏ có thể kết hợp sẽ hiển thị giải thích nhu cầu cấp quyền và một nút để khởi chạy yêu cầu cấp quyền thông qua hệ thống.



Hoạt hình bản đồ

Trong khi hầu hết các ứng dụng bản đồ đều yêu cầu người dùng di chuyển bản đồ thông qua thao tác chạm thì Google Maps for Compose cung cấp API để di chuyển bản đồ theo chương trình. Điều này có thể hữu ích nếu bạn muốn điều hướng đến một khu vực cụ thể để phản hồi một sự kiện.


Trong ví dụ này, chúng tôi sẽ điều hướng ứng dụng một cách nhẹ nhàng thông qua bộ sưu tập điểm đánh dấu của chúng tôi.


 Box(contentAlignment = Alignment.Center) { GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState ) { Clustering(items = parkMarkers, onClusterClick = { // Handle when the click is tapped false }, onClusterItemClick = { marker -> // Handle when the marker is tapped }) LaunchedEffect(key1 = "Animation") { for (marker in parkMarkers) { cameraPositionState.animate( CameraUpdateFactory.newLatLngZoom( marker.itemPosition, // LatLng 16.0f), // Zoom level 2000 // Animation duration in millis ), delay(4000L) // Delay in millis } } } }


Phần quan trọng ở đây là mã bên trong LaunchedEffect . Đối với mỗi điểm đánh dấu, ứng dụng sẽ thiết lập lệnh gọi cameraPositionState.animate() để điều hướng đến điểm đánh dấu. Máy ảnh nhận được thông tin này thông qua bản cập nhật cho máy ảnh, được tạo bằng CameraUpdateFactory.newLatLngZoom() .


Phương thức này lấy LatLng , một float biểu thị mức thu phóng của bản đồ và một khoảng thời gian dài để đặt thời lượng của hoạt ảnh.

Cuối cùng, để giãn cách các hoạt ảnh, chúng ta sử dụng delay() để thêm khoảng dừng 4 giây giữa mỗi hoạt ảnh.


Đang hiển thị Chế độ xem phố

Đây không chỉ là bản đồ trên không mà Google Maps dành cho Compose sẽ giúp bạn. Bạn cũng có thể cấp cho ứng dụng quyền truy cập vào Chế độ xem phố , hiển thị chế độ xem 360 độ của một vị trí. Bạn thực hiện việc này bằng cách sử dụng thành phần kết hợp StreetView :


 var selectedMarker: ParkItem? by remember { mutableStateOf(null) } if (selectedMarker != null) { StreetView(Modifier.fillMaxSize(), streetViewPanoramaOptionsFactory = { StreetViewPanoramaOptions().position(selectedMarker!!.position) }) } else { Box(contentAlignment = Alignment.Center) { GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState ) { Clustering(items = parkMarkers, onClusterClick = { // Handle when the cluster is clicked false }, onClusterItemClick = { marker -> // Handle when a marker in the cluster is clicked selectedMarker = marker false }) } } }


Trong ví dụ này, chúng tôi đang đặt biến selectedMarker bất cứ khi nào điểm đánh dấu được nhấn vào. Nếu điểm đánh dấu được chọn, chúng tôi sẽ hiển thị Chế độ xem phố, chuyển vị trí của điểm đánh dấu.



Vẽ hình

Bạn có thể muốn vẽ các hình dạng và chú thích của riêng mình trên bản đồ. Google Maps dành cho Compose cung cấp một số thành phần kết hợp để thực hiện việc này, trong bài đăng này, chúng ta sẽ sử dụng thành phần kết hợp Circle .


Vòng tròn là hình dạng phù hợp để sử dụng nếu ứng dụng của bạn sử dụng Geofences để phản ứng với những thay đổi từ vị trí của người dùng. Một vòng tròn có thể đại diện cho khu vực mà hàng rào địa lý đang hoạt động bên trong.


 Box(contentAlignment = Alignment.Center) { GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState ) { Clustering(items = parkMarkers, onClusterClick = { // Handle when the cluster is clicked false }, onClusterItemClick = { marker -> // Handle when a marker in the cluster is clicked selectedMarker = marker false }) } } parkMarkers.forEach { Circle( center = it.position, radius = 120.0, fillColor = Color.Green, strokeColor = Color.Green ) }


Ở đây, chúng tôi thiết lập một vòng tròn cho mỗi điểm đánh dấu của mình. Tạo một vòng tròn liên quan đến việc truyền cho nó một vị trí và kích thước bán kính của vòng tròn. Chúng ta cũng sử dụng 2 tham số tùy chọn để thiết lập màu cho đường viền và tô màu cho hình tròn.



Hiển thị một thanh tỷ lệ

Một bản đồ tốt thường đi kèm với chú thích và sơ đồ cho thấy thước đo không gian trên bản đồ tương đương với khoảng cách. Điều này cung cấp cho bạn ý tưởng về các không gian liên quan đến bản đồ vì không phải mọi bản đồ đều có thể sử dụng cùng một hình thức đo lường.


Đối với các bản đồ kỹ thuật số có thể phóng to và thu nhỏ, điều này sẽ tạo thêm một lớp phức tạp cụ thể vì khoảng cách được biểu thị có thể thay đổi linh hoạt. May mắn thay, Google Maps dành cho Compose đã hỗ trợ bạn.


Bằng cách sử dụng thư viện Widgets, bạn có quyền truy cập vào các thành phần kết hợp DisappearingScaleBarScaleBar . Đây là các thành phần giao diện người dùng nằm ở đầu bản đồ, cung cấp cho người dùng thước đo khoảng cách thay đổi tùy thuộc vào mức thu phóng.


 Box(contentAlignment = Alignment.Center) { GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState ) { // You can also use ScaleBar DisappearingScaleBar( modifier = Modifier .padding(top = 5.dp, end = 15.dp) .align(Alignment.TopStart), cameraPositionState = cameraPositionState ) Clustering(items = parkMarkers, onClusterClick = { // Handle when the cluster is clicked false }, onClusterItemClick = { marker -> // Handle when a marker in the cluster is clicked selectedMarker = marker false }) } } parkMarkers.forEach { Circle( center = it.position, radius = 120.0, fillColor = Color.Green, strokeColor = Color.Green ) }


Sau khi thêm thành phần kết hợp, bạn sẽ nhận được Thanh tỷ lệ thay đổi tùy thuộc vào mức thu phóng ở đầu bản đồ.




Giúp đỡ và hỗ trợ

Google Maps dành cho Compose là một cách tuyệt vời để làm việc với Google Maps và còn nhiều điều khác để tìm hiểu. Dưới đây là một số nơi tôi đề xuất nếu bạn cần trợ giúp:

  • Google Maps for Compose Repo : Repo chứa mã nguồn của thư viện. Chứa các mẫu mã về cách sử dụng thư viện và cũng là nơi bạn có thể gửi báo cáo lỗi và đóng góp của mình
  • Trang web Google Maps dành cho Android : Nơi để tìm hiểu các khái niệm đằng sau Google Maps dành cho Android. Đây là những cấp độ cao và không sử dụng thư viện Compose, nhưng vẫn rất quan trọng để biết vì chúng được sử dụng ở chế độ nền.
  • Nền tảng Google Maps Discord Máy chủ Discord chính thức cho Google Maps. Tại đây, bạn có thể tìm thấy mọi người thảo luận về Google Maps cho nhiều nền tảng, yêu cầu và đưa ra trợ giúp cũng như giới thiệu tác phẩm của chính họ.