티스토리 뷰
1) 라이브러리 설치
implementation 'com.google.maps.android:maps-compose:2.5.3'
implementation 'com.google.android.gms:play-services-maps:18.1.0'
2) API 키 발급
api 발급은 어렵지 않으므로 생략하겠습니다.
3) API 셋팅
발급받은 api key는 local.properties에 다음과 같이 저장해 줍니다.
google_map_key=AI...
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
def google_map_key = properties.getProperty('google_map_key')
android {
...
defaultConfig {
...
manifestPlaceholders= [googleMapKey: google_map_key]
...
}
}
발급받은 api key를 Manifest에 사용해야 하므로 설정을 해줍니다.
manifestPlaceholders의 들어가는 키값을 사용할 예정이므로 오타가 없는지 확인 후 복사해 줍니다.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.composestudy">
<application
...
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${googleMapKey}"/>
</application>
</manifest>
meta-data 안에 위에 저장한 키값을 등록해서 api를 설정하게 됩니다.
여기까지 하면 구글 맵을 사용하기 위한 준비가 완료됩니다.
4) 구글 맵 표시
val latLng = LatLng(37.7387295, 127.0458908)
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(latLng, 15f)
}
GoogleMap(
modifier = Modifier.fillMaxSize(),
cameraPositionState = cameraPositionState
) {
Marker(
state = MarkerState(position = latLng),
title = "의정부역",
snippet = "Uijeongbu subway"
)
}
GoogleMap을 사용하면 구글 맵을 앱에 띄울 수 있습니다.
cameraPositionState는 카메라 위치를 저장하는 변수로 position을 지정하여
GoogleMap의 cameraPositionState에 값을 넣어주면 지도의 최초 위치를 지정할 수 있습니다.
Marker를 이용하여 특정 좌표에 표시를 해줄 수 있습니다.
title과 snippet은 마커를 클릭 시 나오는 버블의 문구를 설정할 수 있습니다.
4-1) MarkerInfoWindowContent
MarkerInfoWindowContent(
state = MarkerState(position = latLng),
) { marker ->
Text(marker.title ?: "의정부역", color = Color.Blue)
}
기본 Marker 대신 MarkerInfoWindowContent를 사용하면 버블 내부의 컨탠츠를 꾸밀 수 있습니다.
예시에서는 간단하게 Text를 넣고 색상을 지정해 주었습니다.
이 외에도 Image 등을 넣어서 사용할 수 있습니다.
4-2) MarkerInfoWindow
MarkerInfoWindow(
state = MarkerState(position = latLng)
) { marker ->
Button(
contentPadding = PaddingValues(10.dp),
onClick = {},
shape = RoundedCornerShape(10.dp),
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Yellow
),
) {
Column {
Image(painter = painterResource(id = R.drawable.ic_launcher_background), contentDescription = null)
Text(marker.title ?: "의정부역", color = Color.Blue)
}
}
}
MarkerInfoWindow는 모든 것을 다 커스텀 해서 제작할 수 있는 방식입니다.
4-3) properties와 uiSettings
var mapProperties by remember {
mutableStateOf(
MapProperties(maxZoomPreference = 15f, minZoomPreference = 5f)
)
}
var mapUiSettings by remember {
mutableStateOf(
MapUiSettings(mapToolbarEnabled = false)
)
}
GoogleMap(
properties = mapProperties,
uiSettings = mapUiSettings,
cameraPositionState = cameraPositionState
)
properties는 줌의 최대 크기와 최소 크기를 지정할 수 있습니다.
uiSettings는 각종 맵의 UI의 세팅을 설정할 수 있습니다.
예시로 amppToolbarEnabled의 경우 Marker를 선택 시 아래와 같은 아이콘이 등장합니다.
이 아이콘의 표시 여부를 설정할 수 있습니다.
이 외의 설정을 할 수 있는 종류는 다음과 같습니다.
public class MapUiSettings(
public val compassEnabled: Boolean = true,
public val indoorLevelPickerEnabled: Boolean = true,
public val mapToolbarEnabled: Boolean = true,
public val myLocationButtonEnabled: Boolean = true,
public val rotationGesturesEnabled: Boolean = true,
public val scrollGesturesEnabled: Boolean = true,
public val scrollGesturesEnabledDuringRotateOrZoom: Boolean = true,
public val tiltGesturesEnabled: Boolean = true,
public val zoomControlsEnabled: Boolean = true,
public val zoomGesturesEnabled: Boolean = true,
)
4) 포지션 이동
cameraPositionState.move(CameraUpdateFactory.newLatLng(latLng))
위에서 만든 cameraPositionState를 이용해서 카메라의 포지션을 이동시킬 수 있습니다.
이 외에도 줌 크기 변경, 새로운 포지션 세팅 설정 등이 가능합니다.
5) 활용
간단하게 뷰페이저와 맵을 이용하는 예제입니다.
우선 데이터입니다.
data class MapItem(
val latLng: LatLng,
val title: String,
val image: String
)
fun getMapItemList() = listOf(
MapItem(
latLng = LatLng(37.740118399999744, 127.04342009999966),
title = "스타벅스 의정부공원점",
image = "https://ldb-phinf.pstatic.net/20200825_200/1598301949474sBbkY_JPEG/3509_20171121080426_cnkfi.jpg"
),
MapItem(
latLng = LatLng(37.73786099999975, 127.04186979999947),
title = "스타벅스 신세계의정부",
image = "https://ldb-phinf.pstatic.net/20200825_242/1598302003990GfJlO_JPEG/9698_20180615072632_2qg34.jpg"
),
MapItem(
latLng = LatLng(37.73305020000005, 127.03457659999995),
title = "스타벅스 의정부예술의전당DT점",
image = "https://ldb-phinf.pstatic.net/20210804_47/1628023521693AOigS_JPEG/3571_20210803091352_qzsc3.jpg"
),
)
간단한 예제이므로 데이터는 하드코딩으로 설정하였습니다.
@OptIn(ExperimentalPagerApi::class)
@Composable
fun MapViewPager(
list: List<MapItem>,
state: PagerState,
modifier: Modifier = Modifier
) {
HorizontalPager(
count = list.size,
state = state,
contentPadding = PaddingValues(start = 25.dp, end = 50.dp),
modifier = modifier
) { index ->
MapCard(
item = list[index],
modifier = Modifier
.graphicsLayer {
val pageOffset = calculateCurrentOffsetForPage(index).absoluteValue
lerp(
start = 0.85f,
stop = 1f,
fraction = 1f - pageOffset.coerceIn(0f, 1f)
).also { scale ->
scaleX = scale
scaleY = scale
}
}
)
}
}
@Composable
fun MapCard(
item: MapItem,
modifier: Modifier = Modifier
) {
Card(
shape = RoundedCornerShape(10.dp),
modifier = modifier
.fillMaxWidth()
.padding(end = 10.dp)
) {
Column(
modifier = Modifier.padding(10.dp)
) {
AsyncImage(
model = item.image,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.clip(RoundedCornerShape(10.dp))
)
Spacer(modifier = Modifier.height(10.dp))
Text(text = item.title, fontSize = 16.sp)
}
}
}
다음은 뷰페이저 설정으로 뷰페이저 관련해서는 다른 글에서 다루었으므로 링크를 남기겠습니다.
val list = getMapItemList()
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(list[0].latLng, 14f)
}
val uiSettings by remember {
mutableStateOf(
MapUiSettings(
zoomControlsEnabled = false
)
)
}
val pagerState = rememberPagerState()
val scope = rememberCoroutineScope()
우선 사용하는 변수 대해 간략한 설명을 하자면
list는 위에서 만든 하드코딩 된 데이터를 담은 리스트입니다.
cameraPositionState는 지도의 카메라 상태 관리로,
초기 팅은 리스트의 첫 번째에 담긴 주소를 기준으로 14배 줌을 한 지도를 표시합니다.
uiSettings는 지도의 UI 설정 중 확대/축소 관련 아이콘을 제거하기 위해 사용하였습니다.
pagerState는 Pager의 상태 관리하기 위해 사용하였습니다.
scope는 Pager 이동 시 Coroutine을 사용하기 위해 정의하였습니다.
Box(modifier = Modifier.fillMaxSize()) {
GoogleMap(
cameraPositionState = cameraPositionState,
uiSettings = uiSettings
) {
list.forEachIndexed { index, mapItem ->
MarkerInfoWindow(
state = MarkerState(position = mapItem.latLng),
onClick = {
scope.launch {
pagerState.animateScrollToPage(index)
}
true
}
)
}
}
MapViewPager(
list = list,
state = pagerState,
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 10.dp, max = 200.dp)
.align(Alignment.BottomCenter)
)
LaunchedEffect(pagerState) {
snapshotFlow { pagerState.currentPage }.collect { page ->
cameraPositionState.move(CameraUpdateFactory.newLatLng(list[page].latLng))
}
}
}
마커의 경우 MarkerInfoWindow를 사용한 뒤 Contents에는 아무것도 안 넣음으로써 버블이 발생하지 않습니다.
또한 클릭 시 좌표 위치에 맞는 Pager의 페이지로 이동하게 설정하였습니다.
LaunchedEffect는 Pager 글에는 없던 내용입니다.
Pager의 변화를 감지하기 위해 사용하였습니다.
PagerState의 값이 변경될 때마다 코드 블록이 실행되며, Pager의 현재 위치를 파악한 뒤 지도의 위치를 변경하게 됩니다.
실행 화면입니다.
'안드로이드 > 코드' 카테고리의 다른 글
Compose ConstraintLayout (0) | 2022.11.02 |
---|---|
Compose Naver Map (1) | 2022.10.29 |
Compose LaunchedEffect (0) | 2022.10.27 |
Compose GraphicsLayer (0) | 2022.09.02 |
Compose 기초 4 : Gradient (0) | 2022.09.01 |
- Total
- Today
- Yesterday
- Compose ModalBottomSheetLayout
- LazyColumn
- Compose Naver Map
- 웹뷰
- Compose BottomSheet
- Android
- Compose QRCode Scanner
- Duplicate class found error
- Duplicate class fond 에러
- Compose ConstraintLayout
- Row
- 안드로이드 구글 지도
- 안드로이드
- Compose 네이버 지도 api
- Compose BottomSheetScaffold
- Compose BottomSheetDialog
- 포켓몬 도감
- Fast api
- Compose MotionLayout
- Android Compose
- Kotlin
- Compose 네이버 지도
- Worker
- Retrofit
- compose
- Gradient
- WebView
- WorkManager
- Pokedex
- column
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |