[Android] Jetpack Compose Bottom Navigation
최근 회사에서 새로운 프로젝트를 하고있는데, 그 프로젝트에 처음으로 Jetpack Compose를 적용하고있다.
몇 주간 사용해본 후기로는 기존 xml로 Layout을 짤 때보다 훨씬 편하다고 느끼고 있다.
특히 모바일 앱 화면에서 자주 들어가는 List UI를 짤 때 기존 RecyclerView를 사용할 때보다 훨씬 간단하게 만들어볼 수 있다.
좀 더 간편하게 만들어볼 수 있는 Compose의 요소 중 오늘은 메인화면에 들어갈 Bottom Navigation에 대한 글을 써보려고 한다.
이 글은 Bottom Navigation을 일일이 구현하는 것을 보여주지 않는다.
Navigation의 구성요소를 살펴보고, 내부 코드를 통해 동작 원리를 알아가는데 초점을 맞췄다.
Jetpack Navigation
Navigation은 2018년도에 처음 소개된 Android Jetpack Library이다.
이 Navigation은 Fragment, Activity와 같은 컴포넌트들간의 탐색을 돕기 위해 만들어진 라이브러리이다.
쉽게 말해, UI 전환을 쉽게 구현할 수 있도록 도와준다고 생각하면 된다.
Compose에서도 이 Navigation 라이브러리를 사용해 Bottom Navigation을 구현한다.
#Navigation 구성 요소
Navigation Graph
모든 Navigation 정보가 모여있는 XML 리소스이다.
XML파일로 보면 사용자의 입장에서 화면 전환이 일어나는 모든 플로우를 한 눈에 확인할 수 있는 파일이다.
Jetpack Compose를 사용하는 나는 XML 파일을 따로 생성하지않고, 코드로 구현했다.
@Composable
private fun NavigationGraph(modifier: Modifier, navController: NavHostController) {
NavHost(
navController = navController,
startDestination = MainBottomNavItem.Home.route,
modifier = modifier
) {
//NavGraphBuilder 구현 부분
composable(MainBottomNavItem.Home.route) {
HomeNavScreen()
}
composable(MainBottomNavItem.Search.route) {
SearchNavScreen()
}
composable(MainBottomNavItem.Cart.route) {
CartNavScreen()
}
composable(MainBottomNavItem.MyPage.route) {
MyPageNavScreen()
}
composable(MainBottomNavItem.CustomerCenter.route) {
CustomerCenterNavScreen()
}
}
}
NavigationGraph라는 Composable을 만들고, 그 안에 NavHost Composable을 넣었다.
NavHost의 파라미터에는 NavGraph를 만드는 NavGraphBuilder를 구현하도록 되어있다.
위의 코드에서 composable() 함수를 작성한 부분이 NavGraphBuilder 구현 부분이다.
composable() 함수의 내부 구조를 살펴보면, route(전환할 경로),content(전환할 화면)을 필수 파라미터로 받게 되어있다.
이 route는 우리가 흔히 사용하는 url처럼 해당 화면으로 전환하기 위한 지도 같은 것이라고 생각하면 쉽다.
content는 우리가 인터넷 url을 입력하고 엔터키를 딱 치는 순간 전환될 화면이라고 생각하면 된다:)
이 route와 content를 이용해 addDestination() 함수를 통해 하나의 전환될 화면을 추가해준다.
위의 이미지가 XML로 작성할 때 보여지는 NavGraph의 모습인데, composable() 함수 하나가 저 화면 하나를 추가한다고 생각하면 쉽다.
NavHost
NavHost는 간단하게 실제 화면이 전환되는 본체라고 생각하면 된다.
이 NavHost는 밑에서 설명한 NavController와 위에서 설정한 NavGraph를 가지고 있으며, 이 구성요소(NavController, NavGraph)를 이용해 화면을 전환하는 로직을 실행한다.
내부 코드를 살펴보면, 파라미터로 받는 NavController와 NavGraph를 연결시켜주는 것을 볼 수 있다.
중간 쯤 코드에서는 LifecycleOwner, ViewModelStore도 NavController에 셋팅해주고 있다.
백 스택에 스택이 있을 경우엔 Crossfade 애니메이션 효과를 넣어주는 것을 볼 수 있다.
NavController
쉽게 표현하자면 NavHost를 조종하는 조종사라고 생각하면 된다.
위에서 살펴봤듯이, NavHost 내부 로직에서 NavController와 NavGraph를 연결해주었기 때문에 NavController는 지도를 얻은 셈이다.
NavController는 navigate() 함수가 호출되었을 때 파라미터로 들어오는 route를 가지고 NavGraph에서 해당 route와 맞는 화면으로 전환시키는 역할을 한다.
@Composable
private fun MainBottomNav(
navController: NavController,
currentRoute: String?,
items: List<MainBottomNavItem>
) {
BottomNavigation(
backgroundColor = Color.White
) {
items.forEach { item ->
BottomNavigationItem(
icon = {
Icon(
painter = painterResource(id = item.icon),
contentDescription = stringResource(
id = item.title
)
)
},
label = { Text(text = stringResource(id = item.title)) },
//item이 선택되었을 때 Color
selectedContentColor = Color(0xffEE3139),
//item이 선택되지않았을 때 Color
unselectedContentColor = Color(0xffA6A6A6),
selected = currentRoute == item.route,
onClick = {
//navigate 함수 호출
navController.navigate(item.route) {
navController.graph.startDestinationRoute?.let {
popUpTo(it) { saveState = true }
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
위의 코드를 보면, Bottom Navigation의 Item을 클릭했을 때 NavController의 navigate() 함수를 호출하도록 했다.
launchSingleTop 옵션은 Intent의 FLAG_ACTIVITY_SINGLE_TOP과 비슷한 옵션으로 화면 인스턴스를 한 개만 생성한다는 의미이다.
restoreState 옵션은 다시 그 이전화면으로 돌아갔을 때의 상태를 저장한다는 의미이다.
popUpTo() 함수는 파라미터로 지정한(위의 코드에서는 startDetinationRoute : 처음 시작 화면) destination의 스택을 찾아 그 이후 쌓인 스택은 모두 지우는 기능을 한다. 위의 코드처럼 작성하면 뒤로가기 버튼을 클릭하면 처음 시작 화면으로 돌아간다.
selectedContentColor와 unselectedContentColor는 클릭했을 때와 안했을 때의 컬러를 지정해줄 수 있는 파라미터이다.
@Composable
private fun MainContainer() {
val items = listOf(
MainBottomNavItem.Home,
MainBottomNavItem.Search,
MainBottomNavItem.Cart,
MainBottomNavItem.MyPage,
MainBottomNavItem.CustomerCenter
)
val navController = rememberNavController()
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
Scaffold(
//요기!에 bottom navigation 넣기
bottomBar = {
MainBottomNav(
navController = navController,
currentRoute = currentRoute,
items = items
)
}
) { contentPadding ->
NavigationGraph(
modifier = Modifier
.padding(contentPadding),
navController = navController
)
}
}
Bottom Navigation을 다 만들었다면 Scaffold의 bottomBar 파라미터에 Bottom Navigation Composable을 넣어주면 된다!
Navigation 3줄요약
- NavHost : 본체 및 모니터
- NavGraph : 하드디스크
- NavController : 키보드 및 마우스

'프로그래밍' 카테고리의 다른 글
[Android] Jetpack Compose를 사용해봤더니? (0) | 2022.08.25 |
---|---|
[Git] SourceTree에서 Feature 브랜치 병합 Issue (0) | 2022.08.20 |
안드로이드 과외 첫 수업 리뷰 (0) | 2022.08.16 |
댓글
이 글 공유하기
다른 글
-
[Android] Jetpack Compose를 사용해봤더니?
[Android] Jetpack Compose를 사용해봤더니?
2022.08.25 -
[Git] SourceTree에서 Feature 브랜치 병합 Issue
[Git] SourceTree에서 Feature 브랜치 병합 Issue
2022.08.20 -
안드로이드 과외 첫 수업 리뷰
안드로이드 과외 첫 수업 리뷰
2022.08.16