[Android] Jetpack Navigation + BottomNavigationBar 구현하기

문승연·2024년 2월 22일
0

Jetpack 라이브러리

목록 보기
1/1

1. Single Activity Architecture(SAA)

Single Activity Architecture 이른바 SAA는 단일 혹은 아주 적은 개수의 Activity만 사용하고 모두 Fragment로 화면을 구성하는 App 구조를 말한다.

그리고 주로 Jetpack Navigation과 함께 사용되는 구조이다.

위 내용은 구글 I/O 2018에서 발표된 내용으로 실제로 Jetpack Navigation 관련 문서를 찾아보면 Fragment 탐색에 초점을 두고 있는 것을 알 수 있다.

만약 본인이 만들고 있는 앱이 여러개의 Activity 간 전환이 일어난다면 Navigation은 좋은 선택지가 아니다.

그렇다면 SAA 구조를 사용하는가? SAA 구조를 사용하면서 얻을 수 있는 장점은 다음과 같다.

  1. FragmentActivity 보다 상대적으로 가볍다.
  2. 여러 화면 간 데이터 공유가 용이하다.
  3. Fragment 는 하나의 Activity 안에 여러개가 표시될 수 있다. 따라서 좀 더 동적인 UI를 구현할 수 있다.
  4. 관심사별로 Fragment를 나눠 UI 관심사를 분리할 수 있다.

위와 같이 Activity에 비해 Fragment가 갖는 다양한 이점들 때문에 이런 장점을 적극 활용하기 위해서 나온 개념이 바로 Single Activity Architecture(SAA)라고 할 수 있다.

2. Jetpack Navigation

사실 Jetpack Navigation에 대해서는 이전에도 한번 포스트를 쓴 적이 있다. 하지만 그때 포스트 내용이 부실하기도 하니 이번에 다시 한번 제대로 정리해보기로 했다.

Navigation은 크게 3가지 요소로 이루어져 있다.

  1. NavHost: 현재 탐색 대상(navigation destination)이 포함된 UI 요소. 즉, 사용자가 앱을 탐색할 때 앱은 기본적으로 NavHost 안팎으로 대상을 전환한다.

  2. NavGraph: 앱 내의 탐색 대상들(Fragment)과 어떻게 연결되어있는지 등이 정의되어있는 자료구조. 앱 내의 화면 연결을 보여주는 일종의 지도 역할을 한다.

  3. NavController: 실질적으로 탐색 동작을 처리하는 역할. 대상을 탐색하고 딥 링크를 처리하며 대상 백 스택을 관리하는 등의 여러 메소드를 제공한다.

3. BottomNavigationBar 구현하기

이제 위에서 설명한 Navigation과 Compose를 활용해 BottomNavigationBar를 구현해보자.

1. gradle에 라이브러리 추가

implementation("androidx.navigation:navigation-compose:2.7.7")

Compose-Navigation 라이브러리를 gradle에 추가한다.

2. Bottom Navigation으로 이동할 화면 생성.

필자는 여기서 Home, Rating, Profile 3개의 화면을 만들어주었다.

@Composable
fun HomeScreen() {
    Text(text = "Home")
}

@Composable
fun RatingScreen() {
    Text(text = "Rating")
}

@Composable
fun ProfileScreen() {
    Text(text = "Profile")
}

3. BottomNavItem 생성.

Bottom Navigation으로 이동할 item 객체 클래스 BottomNavItem 클래스를 생성하고 그 자식으로 Home, Rating, Profile 을 추가한다.

sealed class BottomNavItem(
    @StringRes val title: Int,
    @DrawableRes val icon: Int,
    val screenRoute: String
) {
    object Home : BottomNavItem(R.string.home, R.drawable.ic_home, LiamScreens.Home.name)
    object Rating : BottomNavItem(R.string.rating, R.drawable.ic_rating, LiamScreens.Rating.name)
    object Profile : BottomNavItem(R.string.profile, R.drawable.ic_profile, LiamScreens.Profile.name)
}

4. NavHost, NavGraph 생성.

NavHost 함수의 NavGraphBuilder 를 통해서 NavGraph를 생성할 수 있다.

NavHost(
    modifier = modifier,
    navController = navController,
    startDestination = startDestination
) {
    composable(route = MyScreens.Sample.name) {
        SampleScreen()
    }

    composable(route = MyScreens.Home.name) {
        HomeScreen()
    }

    composable(route = MyScreens.Rating.name) {
        RatingScreen()
    }

    composable(route = MyScreens.Profile.name) {
        ProfileScreen()
    }

    composable(route = MyScreens.Search.name) {
        SearchScreen()
    }
}

5. Bottom Navigation 생성

@Composable
fun MyApp() {
    val navController = rememberNavController()

    Scaffold(
        modifier = Modifier.fillMaxSize(),
        bottomBar = {
            MyBottomNavigation(
                containerColor = Color.Green,
                contentColor = Color.White,
                indicatorColor = Color.Green,
                navController = navController
            )
        }
    ) {
        Box(modifier = Modifier.padding(it)) {
            MyNavHost(
                navController = navController,
                startDestination = LiamScreens.Home.name
            )
        }
    }
}

@Composable
private fun MyNavHost(
    modifier: Modifier = Modifier,
    navController: NavHostController,
    startDestination: String
) {
    NavHost(
        modifier = modifier,
        navController = navController,
        startDestination = startDestination
    ) {
        composable(route = LiamScreens.Sample.name) {
            SampleScreen()
        }

        composable(route = LiamScreens.Home.name) {
            HomeScreen(
                onSearchClicked = { navController.navigate(LiamScreens.Search.name) }
            )
        }

        composable(route = LiamScreens.Rating.name) {
            RatingScreen()
        }

        composable(route = LiamScreens.Profile.name) {
            ProfileScreen()
        }

        composable(route = LiamScreens.Search.name) {
            SearchScreen()
        }
    }
}

@Composable
private fun MyBottomNavigation(
    modifier: Modifier = Modifier,
    containerColor: Color,
    contentColor: Color,
    indicatorColor: Color,
    navController: NavHostController
) {
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    val currentRoute = navBackStackEntry?.destination?.route
    val items = listOf(
        BottomNavItem.Home,
        BottomNavItem.Rating,
        BottomNavItem.Profile
    )

    AnimatedVisibility(
        visible = items.map { it.screenRoute }.contains(currentRoute)
    ) {
        NavigationBar(
            modifier = modifier,
            containerColor = containerColor,
            contentColor = contentColor,
        ) {
            items.forEach { item ->
                NavigationBarItem(
                    selected = currentRoute == item.screenRoute,
                    label = {
                        Text(
                            text = stringResource(id = item.title),
                            style = TextStyle(
                                fontSize = 12.sp
                            )
                        )
                    },
                    icon = {
                        Icon(
                            painter = painterResource(id = item.icon),
                            contentDescription = stringResource(id = item.title)
                        )
                    },
                    onClick = {
                        navController.navigate(item.screenRoute) {
                            navController.graph.startDestinationRoute?.let {
                                popUpTo(it) { saveState = true }
                            }
                            launchSingleTop = true
                            restoreState = true
                        }
                    },
                )
            }
        }
    }
}

레퍼런스
1. 안드로이드 공식 홈페이지 [Navigation]
2. [Android] Jetpack Compose - Bottom Navigation 만들기
3. [Android] Single Activity Architecture (SAA) + Navigation
4. Navigation 훑어보기

profile
"비몽(Bemong)"이라는 앱을 개발 및 운영 중인 안드로이드 개발자입니다.

0개의 댓글