Google Maps
Bài này hướng dẫn bạn tích hợp Google Maps vào ứng dụng Android với Jetpack Compose, từ setup cơ bản đến các tính năng nâng cao.
1. Tại sao dùng Google Maps?
Phần tiêu đề “1. Tại sao dùng Google Maps?”Google Maps cho phép bạn:
- Hiển thị bản đồ trong app
- Đánh dấu vị trí (markers)
- Hiển thị vị trí hiện tại của user
- Vẽ đường đi, polylines
- Tích hợp với Places API để tìm kiếm địa điểm
2. Setup Google Maps SDK
Phần tiêu đề “2. Setup Google Maps SDK”Bước 1: Tạo API Key
Phần tiêu đề “Bước 1: Tạo API Key”- Truy cập Google Cloud Console
- Tạo project mới hoặc chọn project có sẵn
- Vào APIs & Services > Library
- Enable Maps SDK for Android
- Vào APIs & Services > Credentials
- Click Create Credentials > API Key
- Copy API key
Bước 2: Thêm Dependencies
Phần tiêu đề “Bước 2: Thêm Dependencies”// build.gradle.kts (Module: app)dependencies { // Google Maps Compose implementation("com.google.maps.android:maps-compose:4.3.0")
// Google Maps Services (optional, for places) implementation("com.google.android.gms:play-services-maps:18.2.0") implementation("com.google.android.gms:play-services-location:21.1.0")}Bước 3: Cấu hình API Key
Phần tiêu đề “Bước 3: Cấu hình API Key”<application ...> <meta-data android:name="com.google.android.geo.API_KEY" android:value="${MAPS_API_KEY}" /></application>// local.properties (KHÔNG commit lên Git!)MAPS_API_KEY=AIzaSyB...your_api_key_hereandroid { defaultConfig { manifestPlaceholders["MAPS_API_KEY"] = project.findProperty("MAPS_API_KEY") as String? ?: "" }}Tại sao làm như vậy?
- API key là thông tin nhạy cảm
- Không nên commit trực tiếp vào code
local.propertiesđã được ignore bởi.gitignore
3. Hiển thị bản đồ cơ bản
Phần tiêu đề “3. Hiển thị bản đồ cơ bản”@Composablefun BasicMapScreen() { // Vị trí mặc định: Hà Nội val hanoi = LatLng(21.0285, 105.8542)
// Camera state - điều khiển góc nhìn của bản đồ val cameraPositionState = rememberCameraPositionState { position = CameraPosition.fromLatLngZoom(hanoi, 12f) }
GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState )}Giải thích:
LatLng(21.0285, 105.8542): Tọa độ (vĩ độ, kinh độ) của Hà Nộizoom = 12f: Mức zoom (1 = thế giới, 20 = tòa nhà)cameraPositionState: Quản lý vị trí camera (có thể animate)
4. Thêm Markers (Đánh dấu vị trí)
Phần tiêu đề “4. Thêm Markers (Đánh dấu vị trí)”@Composablefun MapWithMarkers() { val hanoi = LatLng(21.0285, 105.8542) val hoGuom = LatLng(21.0288, 105.8525) val hoTay = LatLng(21.0542, 105.8231)
val cameraPositionState = rememberCameraPositionState { position = CameraPosition.fromLatLngZoom(hanoi, 13f) }
GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState ) { // Marker cơ bản Marker( state = MarkerState(position = hoGuom), title = "Hồ Gươm", snippet = "Trung tâm Hà Nội" )
// Marker với icon tùy chỉnh Marker( state = MarkerState(position = hoTay), title = "Hồ Tây", icon = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE) ) }}Giải thích:
Marker: Đánh dấu một vị trí trên bản đồtitle: Tiêu đề hiển thị khi tap vào markersnippet: Mô tả ngắn bên dưới titleicon: Có thể dùng màu khác hoặc icon tùy chỉnh
5. Lấy vị trí hiện tại của User
Phần tiêu đề “5. Lấy vị trí hiện tại của User”Bước 1: Thêm Permissions
Phần tiêu đề “Bước 1: Thêm Permissions”<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />Bước 2: Request Permission và lấy vị trí
Phần tiêu đề “Bước 2: Request Permission và lấy vị trí”@Composablefun MapWithCurrentLocation() { val context = LocalContext.current var currentLocation by remember { mutableStateOf<LatLng?>(null) }
// Request location permission val permissionState = rememberPermissionState(Manifest.permission.ACCESS_FINE_LOCATION)
LaunchedEffect(permissionState.status.isGranted) { if (permissionState.status.isGranted) { // Lấy vị trí hiện tại val fusedLocationClient = LocationServices.getFusedLocationProviderClient(context) fusedLocationClient.lastLocation.addOnSuccessListener { location -> location?.let { currentLocation = LatLng(it.latitude, it.longitude) } } } }
// UI Column { if (!permissionState.status.isGranted) { Button(onClick = { permissionState.launchPermissionRequest() }) { Text("Cho phép truy cập vị trí") } }
currentLocation?.let { location -> val cameraPositionState = rememberCameraPositionState { position = CameraPosition.fromLatLngZoom(location, 15f) }
GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState, properties = MapProperties(isMyLocationEnabled = true) ) { Marker( state = MarkerState(position = location), title = "Vị trí của bạn" ) } } }}Giải thích từng bước:
- Kiểm tra permission đã được cấp chưa
- Nếu chưa → hiển thị button để request
- Nếu có → dùng
FusedLocationProviderClientđể lấy vị trí isMyLocationEnabled = truehiển thị chấm xanh vị trí hiện tại
6. Xử lý khi User tap vào bản đồ
Phần tiêu đề “6. Xử lý khi User tap vào bản đồ”@Composablefun MapWithTapHandler() { var selectedLocation by remember { mutableStateOf<LatLng?>(null) }
val cameraPositionState = rememberCameraPositionState { position = CameraPosition.fromLatLngZoom(LatLng(21.0285, 105.8542), 12f) }
GoogleMap( modifier = Modifier.fillMaxSize(), cameraPositionState = cameraPositionState, onMapClick = { latLng -> // User tap vào vị trí này selectedLocation = latLng } ) { selectedLocation?.let { location -> Marker( state = MarkerState(position = location), title = "Vị trí đã chọn", snippet = "Lat: ${location.latitude}, Lng: ${location.longitude}" ) } }}7. Vẽ đường (Polyline)
Phần tiêu đề “7. Vẽ đường (Polyline)”@Composablefun MapWithRoute() { // Các điểm tạo thành đường đi val routePoints = listOf( LatLng(21.0285, 105.8542), // Điểm A LatLng(21.0300, 105.8500), // Điểm B LatLng(21.0350, 105.8450), // Điểm C LatLng(21.0400, 105.8400), // Điểm D )
GoogleMap(...) { // Vẽ đường nối các điểm Polyline( points = routePoints, color = Color.Blue, width = 10f )
// Đánh dấu điểm đầu và cuối Marker(state = MarkerState(position = routePoints.first()), title = "Điểm đi") Marker(state = MarkerState(position = routePoints.last()), title = "Điểm đến") }}8. Các loại Map (Map Types)
Phần tiêu đề “8. Các loại Map (Map Types)”GoogleMap( properties = MapProperties( mapType = MapType.NORMAL // Bản đồ thường // mapType = MapType.SATELLITE // Ảnh vệ tinh // mapType = MapType.TERRAIN // Địa hình // mapType = MapType.HYBRID // Kết hợp ))9. Map Controls (Zoom, Compass, etc.)
Phần tiêu đề “9. Map Controls (Zoom, Compass, etc.)”GoogleMap( uiSettings = MapUiSettings( zoomControlsEnabled = true, // Nút +/- compassEnabled = true, // La bàn myLocationButtonEnabled = true, // Nút về vị trí hiện tại mapToolbarEnabled = false // Toolbar Google Maps ))📝 Tóm tắt cho người mới
Phần tiêu đề “📝 Tóm tắt cho người mới”| Bước | Làm gì |
|---|---|
| 1 | Tạo API key trên Google Cloud Console |
| 2 | Thêm dependency maps-compose |
| 3 | Cấu hình API key trong Manifest |
| 4 | Dùng GoogleMap composable |
| 5 | Thêm Marker để đánh dấu vị trí |
| 6 | Request permission để lấy vị trí user |
Lưu ý bảo mật
Phần tiêu đề “Lưu ý bảo mật”- Không commit API key lên GitHub
- Sử dụng
local.propertieshoặc environment variables - Có thể restrict API key trong Google Cloud Console