[Android]Jetpack Compose Bottom navigation

KIMGEUNTAE·2024년 7월 1일
2

Android

목록 보기
9/10
post-thumbnail

기존 Xml로 바텀 네비게이션을 개발 할 때에는 해당 액티비티의 xml에 바텀 네비게이션과 NavHostFragment를 설정하고 xml에 폴더에 navigtaion을 추가하여 개발을 진행 했지만 Jetpack Compose에서는 선언형 UI로 코틀린 코드로 작성하므로 코드 양도 줄고 일관성이 높아지게 되었다.


📌 프로젝트 설정

1. libs.versions

  • 먼저 최근에는 라이브러리 추가를 이렇게 libs.versions에 해당 라이브러리 버전과 플러그인 라이브러리를 추가를 하고 gradle에 이름을 입력하여 사용 하고 있다.
  • 이 방식이 권장되는 이유는 종속성 관리 및 버전과의 충돌도 최소화 되고 유지보수에 용이 함에 있어 최근에는 이 방식이 많이 사용되고 있다고 한다.

2. build.gradle.kts

  • implementation(libs.navigation.compose) : 기본적인 네비게이션 관련 컴포넌트를 제공

  • implementation(libs.hilt.navigation.compose) : Hilt와 Jetpack 네비게이션을 통합하는 라이브러리이며 hiltViewModel() 함수를 사용 할 수 있게 하여 네비게이션에 쉽게 뷰모델을 주입 할 수가 있어진다.


3. build.gradle.kts(Project)



📌 Hilt 설정

1. 프로젝트에 Hilt를 설정

@HiltAndroidApp
class App : Application() {}
  • Application 클래스를 생성하고 @HiltAndroidApp 어노테이션을 추가합니다.

2. 메인 액티비티 설정

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            JetPack_BottomNavigationTheme {
                MainScreen()
            }
        }
    }
}
  • Application 클래스를 생성하고 @HiltAndroidApp 어노테이션을 추가합니다.

3. ViewModel 설정

@HiltViewModel
class HomeViewModel @Inject constructor() : ViewModel() {}
  • ViewModel을 생성하고 @HiltViewModel 어노테이션을 추가합니다.


📌 바텀 네비게이션 설정

1. 바텀 네비게이션 아이템 설정

sealed class BottomNavItem(val route: String, val icon: ImageVector, val title: String) {
    data object Home : BottomNavItem("home", Icons.Default.Home, "Home")
    data object Search : BottomNavItem("search", Icons.Default.Search, "Search")
    data object Profile : BottomNavItem("profile", Icons.Default.Person, "Profile")
}

해당 방식은 sealed class 방식으로 각 객체에 대해 다른 프로퍼티나 메서드를 추가할 수 있어 더 유연하지만 단순히 고정된 값 집합이 필요하다면 enum class가 더 적합하며 향 후 추가적인 동작이나 속성이 필요한 경우 sealed class가 유연하다.

  • 생성자 매개변수
    - route : 각 화면의 고유한 경로를 나타내는 문자열
    - icon : 각 항목의 아이콘을 나타내는 ImageVector 객체
    - title : 각 항목의 제목을 나타내는 문자열

  • data object 키워드
    - Kotlin 1.9부터 도입된 새로운 키워드
    - 싱글톤 객체를 생성하면서 동시에 equals(), hashCode(), toString() 메서드를 자동으로 생성


2. 바텀 네비게이션 컴포저블 작성

@Composable
fun BottomNavigation(navController: NavHostController) {
    val items = listOf(
        BottomNavItem.Home,
        BottomNavItem.Search,
        BottomNavItem.Profile
    )
    NavigationBar {
        val navBackStackEntry by navController.currentBackStackEntryAsState()
        val currentRoute = navBackStackEntry?.destination?.route
        items.forEach { item ->
            NavigationBarItem(
                icon = { Icon(item.icon, contentDescription = item.title) },
                label = { Text(item.title) },
                selected = currentRoute == item.route,
                onClick = {
                    navController.navigate(item.route) {
                        popUpTo(navController.graph.findStartDestination().id) {
                            saveState = true
                        }
                        launchSingleTop = true
                        restoreState = true
                    }
                }
            )
        }
    }
}

해당 함수에서 네비게이션 바를 구성하여 화면간의 이동을 가능하게 하며 네비게이션의 바의 항목들은 BottomNavItem 클래스에서 정의된 화면으로 구성

  • items : 바텀 네비게이션에 표시될 항목들을 리스트로 정의
  • navBackStackEntry : 백스택 상태를 관찰하여 화면이 변경 될때마다 값이 업데이트 된다.
  • currentRoute : 활성 화된 경로를 가져옴
  • selected = currentRoute == item.route : 현재 경로가 이 항목의 경로와 일치하면 선택된 상태로 표시
  • onClick {...} : 항목이 클릭 시 실행 동작을 정의하며 popUpTo 부분에 시작과 목적지까지 백 스택을 팝업하고 상태를 저장하며 이는 스택이 쌓이는 것을 방지한다.
  • launchSingleTop = true : 동일한 목적지가 이미 스택의 최상위에 있다면 새 인스턴스를 만들지 않음
  • restoreState = true : 이전의 저장된 상태를 복원

3. 메인 화면 설정

@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@Composable
fun MainScreen() {
    val navController = rememberNavController()
    Scaffold(
        bottomBar = { BottomNavigation(navController) }
    ) {
        NavHost(
            navController = navController,
            startDestination = BottomNavItem.Home.route
        ) {
            composable(BottomNavItem.Home.route) { HomeScreen() }
            composable(BottomNavItem.Search.route) { SearchScreen() }
            composable(BottomNavItem.Profile.route) { ProfileScreen() }
        }
    }
}

Scaffold를 사용하여 기본 레이아웃을 만들고 하단에 네비게이션 바를 배치합니다. NavHost를 통해 각 경로에 해당하는 화면을 정의하여 사용자가 바텀 네비게이션을 통해 다른 화면으로 이동할 수 있게 합니다.

  • val navController = rememberNavController() :
    네비게이션 컨트롤러를 생성하고 기억 하여 화면 간 이동을 관리
  • bottomBar = { BottomNavigation(navController) } :
    Scaffold의 하단에 앞서 정의한 BottomNavigation 컴포저블을 배치
  • NavHost(...) : 네비게이션 그래프를 정의하는 컴포저블로 각 경로에 해당하는 컴포저블을 지정
  • navController = navController : 활성 화된 경로를 가져옴
  • startDestination = BottomNavItem.Home.route :
    앱이 시작될 때 처음 표시될 화면의 경로를 지정
  • lcomposable(...) : 각 네비게이션 항목에 대한 목적지를 지정

4. 화면 컴포저블 작성

@Composable
fun HomeScreen(homeViewModel: HomeViewModel = hiltViewModel()) {
    Text("Home Screen")
}

@Composable
fun SearchScreen() {
    Text(text = "Search Screen")
}

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

해당 바텀네비게이션의 각 아이템의 화면이며 현재는 매우 간단한 형태이지만 실제 앱에서는 더욱 확장 될 수 있습니다.

  • 각 화면에 더 복잡한 UI 요소들을 추가할 수 있습니다.
  • ViewModel을 통해 데이터를 로드하고 상태를 관리
  • 사용자 입력을 처리하고 해당 입력에 따라 UI를 업데이트할 수 있음


📌 마무리

최근 서브 프로젝트에 참여하면서 Jetpack Compose와 Hilt를 사용하여 바텀 네비게이션을 작업 하였는데 기존 XMl에 익숙해서 그런가 Jetpack Compose로 하는 것이 코드가 간결하다는데 익숙하지 않았다.
그래도 네비게이션에 Hilt를 사용하여 의존성 주입을 간편하게 처리할 수 있어 코드의 유지보수성과 확장성을 높일 수 있을 것 같다.


깃허브 : https://github.com/GEUN-TAE-KIM/Jetpack-Compose_BottomNavigation_Sample

profile
Study Note

0개의 댓글