노트: 내비게이션 그래프는 사용자가 최근에 방문한 목적지를 보관하는 스택인 백스택과는 별개입니다.
호스팅된 목적지: 내비게이션 호스트 전체를 채움, 즉, 호스팅된 목적지가 내비게이션 호스트와 같은 사이즈며 이전 목적지는 보이지 않게 됨, 메인 및 상세 화면의 경우에 사용됨
대화상자 목적지: 오버레이 UI 컴포넌트를 제공함, 이 UI는 내비게이션 호스트의 크기나 위치에 묶이지 않으며 이전 목적지가 이 목적지 아래에 보이게 됨, 알림, 선택, 양식 창을 띄우는 경우에 사용됨
액티비티 목적지: 앱 내의 고유의 화면이나 기능을 나타냄, 내비게이션 그래프의 종료 지점과 같이 취급되며 내비게이션 컴포넌트와 독립적으로 관리되는 새로운 액티비티를 시작함, 최근 안드로이드 개발에서는 앱이 단일 액티비티로 이루어지므로 다른 앱의 액티비티나 마이그레이션 과정의 일부로 사용하기 적합함
이 문서에서는 호스팅된 목적지의 예시를 제공며 이는 가장 기본적이고 많이 쓰이는 목적지임, 대화상자 목적지나, 액티비티 목적지는 별도의 문서가 있음
Compose: NavHost 컴포저블을 사용함, 코틀린 DSL을 사용해 NavGraph 를 추가함, 그래프를 다음 두가지 방법 중 하나로 생성 가능함
NavController.createGraph() 메소드를 사용해 NavGraph 를 생성하고 NavHost 에 전달하는 방법프래그먼트: 뷰 UI 프레임워크를 사용한 프래그먼트를 사용한다면 NavHostFragment 를 호스트로 사용함, 다음과 같은 방법들로 내비게이션 그래프를 생성함
NavGraph 를 생성하고 직접 NavHostFragment 에 적용함createGraph() 함수가 사용되는 것은 프래그먼트와 Compose에서 동일함노트: NavController를 통해 그래프와 상호착용을 하는 방법은 프레임워크간에 비슷합니다. 세부 정보는 목적지로 탐색하기를 확인하십시오.
Compose에서 경로를 정의하기 위해 직렬화 가능한 객체나 클래스를 사용함, 경로는 목적지로 도달하는 방법을 묘사하며 목적지가 필요로하는 모든 정보를 포함함
@Serializable 어노테이션을 사용하면 경로 유형을 위해 필요한 직렬화, 역직렬화 메소드를 자동으로 생성해줌, 이 주석은 코틀린 직렬화 플러그인에서 제공함, 코틀린 직렬화 플러그인 추가를 따라서 플러그인을 추가할 수 있음
NavHost 컴포저블을 사용해 내비게이션 그래프를 생성하는 예시
@Serializable
object Profile
@Serializable
object FriendsList
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Profile) {
composable<Profile> { ProfileScreen( /* ... */ ) }
composable<FriendsList> { FriendsListScreen( /* ... */ ) }
// 위와 비슷하게 목적지를 추가할 수 있습니다.
}
직렬화 가능한 객체는 각각 두 경로 Profile 과 FriendList를 나타냄
NavHost 컴포저블을 호출할 때 NavController 와 시작 목적지의 경로를 전달함
NavHost 에 전달되는 람다 함수는 궁극적으로 NavController.createGraph() 를 호출하고 NavGraph 를 반환함
각 경로는 NavGraphBuilder.composable<T>() 에 유형 인자로 전달되며 이는 결과적으로 생성되는 NavGraph 에 목적지를 추가함
람다에 전달되는 composable 은 NavHost 가 그 목적지에서 표시해야 하는 것
주의:
composable()에 유형 대신 경로의 문자열 또는 정수id를 전달할 수 있습니다. 하지만 이는 목적지로 전달하는 추가 인자를 관리하기 힘들게 만듭니다.
NavGraph 를 생성하는 람다를 더 잘 이해하려면 다음 코드와 같이 NavController.createGraph() 를 독립적으로 사용해 NavGraph 를 만들고 NavHost 에 직접 전달할 수 있다는 점을 생각하면 됨val navGraph by remember(navController) {
navController.createGraph(startDestination = Profile)) {
composable<Profile> { ProfileScreen( /* ... */ ) }
composable<FriendsList> { FriendsListScreen( /* ... */ ) }
}
}
NavHost(navController, navGraph)
중요:
NavController는 단일NavHost컴포저블과 연결되어 있습니다.NavHost는NavController에 자신의 내비게이션 그래프에 대한 접근을 제공합니다.NavController를 사용해 목적지로 탐색을 하도록 하면NavController가 연결된NavHost와 상호작용하도록 만드는 것입니다.
name 인자를 가지는 Profile 경로의 데이터 클래스의 예시임@Serializable
data class Profile(val name: String)
노트: 인자가 필요한 경로에
data class를 사용하고, 인자가 필요하지 않은 경로에는object나data object를 사용하십시오.
optional) 인자를 위해서는 다음 예시와 같이 기본 값을 가지며 null을 가질 수 있는 필드로 만들면 됨@Serializable
data class Profile(val nickname: String? = null)
NavBackStackEntry.toRoute() 또는 SavedStateHandle.toRoute() 를 이용해 경로 인스턴스를 얻을 수 있음, composable() 을 사용해 목적지를 생성하면 NavBackStackEntry 는 다음 예시처럼 파라미터로 사용이 가능함@Serializable
data class Profile(val name: String)
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Profile(name="John Smith")) {
composable<Profile> { backStackEntry ->
val profile: Profile = backStackEntry.toRoute()
ProfileScreen(name = profile.name) }
}
Profile 경로는 내비게이션 그래프의 시작 목적지이며 name 인수에 "John Smith" 값을 가지고 있음
composable<Profile>() 블록이 목적지임
ProfileScreen 컴포저블은 profile.name 값을 자신의 name 인자 값으로 가져감, 이로 인해 "John Smith" 값이 ProfileScreen 으로 전달됨
NavController 와 NavHost 를 같이 사용하는 예시임@Serializable
data class Profile(val name: String)
@Serializable
object FriendsList
// ProfileScreen 컴포저블을 정의합니다.
@Composable
fun ProfileScreen(
profile: Profile
onNavigateToFriendsList: () -> Unit,
) {
Text("Profile for ${profile.name}")
Button(onClick = { onNavigateToFriendsList() }) {
Text("Go to Friends List")
}
}
// FriendsListScreen 컴포저블을 정의합니다.
@Composable
fun FriendsListScreen(onNavigateToProfile: () -> Unit) {
Text("Friends List")
Button(onClick = { onNavigateToProfile() }) {
Text("Go to Profile")
}
}
// `NavController` 와 `NavHost` 를 포함하는 MyApp 컴포저블을 정의합니다.
@Composable
fun MyApp() {
val navController = rememberNavController()
NavHost(navController, startDestination = Profile(name = "John Smith")) {
composable<Profile> { backStackEntry ->
val profile: Profile = backStackEntry.toRoute()
ProfileScreen(
profile = profile,
onNavigateToFriendsList = {
navController.navigate(route = FriendsList)
}
)
}
composable<FriendsList> {
FriendsListScreen(
onNavigateToProfile = {
navController.navigate(
route = Profile(name = "Aisha Devi")
)
}
)
}
}
}
NavController 를 직접 전달하는 대신 NavHost 에 이벤트를 노출함, 컴포저블이 () -> Unit 유형의 인자를 가지도록 해 NavHost 가 NavController.navigate() 를 호출하는 람다를 전달하도록 함노트: 내비게이션 컴포넌트는 다수의 프래그먼트 목적지를 가지는 단일의 메인 액티비티를 가지는 앱을 위해 설계되었습니다. 메인 액티비티는 필요에 따라 목적지를 전환하는 역할을 하는
NavHostFragment를 포함하는 내비게이션 그래프와 연결되어 있습니다. 다수의 액티비티 목적지를 가지는 앱은 각 액티비티가 자신의 내비게이션 그래프를 가지고 있습니다.
다음은 두 화면이 있는 내비게이션 그래프를 구현하는 예제임
NavHostFragment 를 생성해야 함, 이는 app:navGraph 요소에 포함되지 않아야 함<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
다음으로 NavHostFragment 의 id 를 NavController.findNavController() 에 전달해야함, 이는 NavHostFragment 와 NavController 를 연결하는 작업임
그 후에 NavController.creageGraph() 를 호출해 그래프를 NavController 에 연결하고 이는 결과적으로 NavHostFragment 에 연결하는 것이 됨
@Serializable
data class Profile(val name: String)
@Serializable
object FriendsList
// NavController를 전달받음.
val navController = findNavController(R.id.nav_host_fragment)
// `createGraph()`로 NavController에 그래프를 추가한다.
navController.graph = navController.createGraph(
startDestination = Profile(name = "John Smith")
) {
// 각 목적지를 경로 상수 중 하나와 연결한다.
fragment<ProfileFragment, Profile> {
label = "Profile"
}
fragment<FriendsListFragment, FriendsList>() {
label = "Friends List"
}
// 다른 프래그먼트 목적지도 비슷하게 추가한다.
}
Compose 에서 사용하는 방법과 비슷한 방식임, 예시로 둘 다 NavGraph 를 생성하기 위해 NavController.createGraph() 함수를 사용함, 마찬가지로 Compose 에서 NavGraphBuilder.composable() 가 그래프에 컴포저블 목적지를 추가하는 것처럼, 여기서는 NavGraphBuilder.fragment() 를 사용해 프래그먼트 목적지를 추가함직접 XML을 작성할 수도 있음, 다음 예시는 이전 부분에서 다루었던 두 화면을 가지는 그래프 예시와 동일한 역할을 하는 예시임
먼저 NavHostFragment 를 생성함, 이는 실제 내비게이션 그래프를 포함하는 내비게이션 호스트 역할을 함
NavHostFragment 의 최소 구현 예시
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navGraph="@navigation/nav_graph" />
</FrameLayout>
NavHostFragment 는 app:navGraph 속성을 포함함, 이 속성을 사용해 내비게이션 그래프를 연결할 수 있음, 다음 예제는 그래프를 구현하는 방법을 보여줌<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph"
app:startDestination="@id/profile">
<fragment
android:id="@+id/profile"
android:name="com.example.ProfileFragment"
android:label="Profile">
<!-- Profile 에서 Friends List로 탐색하는 액션입니다. -->
<action
android:id="@+id/action_profile_to_friendslist"
app:destination="@id/friendslist" />
</fragment>
<fragment
android:id="@+id/friendslist"
android:name="com.example.FriendsListFragment"
android:label="Friends List" />
<!-- 다른 프래그먼트 목적지를 비슷하게 추가할 수 있습니다. -->
</navigation>
profile 프래그먼트가 friendslist 로 탐색하는 액션을 포함함노트: DSL 예제에서는 이 개념이 적용되지 않기 때문에 액션을 적용하지 않습니다. DSL을 사용중이라면
NavController.navigate()를 사용하세요.