티스토리 뷰
Navigation
앱 내의 화면을 들어갔다 나올 수 있게 플로우를 정한다.
일반적으로 화면을 전환할 수 있는 하단바를 의미함.
Navigation 구성요소
- Navigation Graph: 모든 Navigation 관련 정보가 한 곳에 모여있는 xml 파일. 사용자가 앱에서 갈 수 있는 모든 경로를 보여준다.
- NavHost: Navigation Graph에서 화면을 표시하는 빈 컨테이너.
- NavController: NavHost에서 앱 Navigation을 관리하는 객체. 사용자가 앱 내에서 화면을 이동할 때 NavHost에서 화면 전환을 controll하는 역할.
Navigation Graph에서 어떤 경로로 이동할지 NavController에게 전달하고, NavController는 NavHost에게 적절한 화면을 전달해준다. NavHost에서는 전달받은 화면을 띄운다.
Navigation 장점
- 프래그먼트 트랜잭션 처리
- '위로'와 '뒤로' 작업을 올바르게 처리
- 화면 전환에 대한 애니메이션
- 딥링크 구현 및 처리
- Navigation UI 패턴들을 편리하게 추가
- Safe Args - 화면 간 데이터를 전달할 때 type safety을 제공하는 Gradle plugin
- ViewModel - Navigation Graph 에 대한 ViewModel을 확인해 화면 사이에 UI 관련 데이터를 공유
Navigation 시작하기
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
module 수준의 builde.gradle에 위와 같은 의존성을 추가한다.
Navigation Graph 생성하기
- res 디렉터리 마우스 오른쪽 버튼, New > Android Resource File 선택
- File name 필드에 'nav_graph'와 같은 이름을 입력
- Resource type 드롭다운 목록에서 Navigation을 선택한 후 OK를 클릭
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph">
</navigation>
처음엔 아무 화면도 추가하지 않았기 때문에 위와 같은 xml 내용이 된다.
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@+id/navigation_home">
<fragment
android:id="@+id/navigation_home"
android:name="com.example.myapplication.ui.home.HomeFragment"
android:label="@string/title_home"
tools:layout="@layout/fragment_home" />
<fragment
android:id="@+id/navigation_dashboard"
android:name="com.example.myapplication.ui.dashboard.DashboardFragment"
android:label="@string/title_dashboard"
tools:layout="@layout/fragment_dashboard" />
<fragment
android:id="@+id/navigation_notifications"
android:name="com.example.myapplication.ui.notifications.NotificationsFragment"
android:label="@string/title_notifications"
tools:layout="@layout/fragment_notifications" />
</navigation>
navigation bar로 3개의 fragment를 전환하도록 하고 싶다면, 위와 같이 3개의 fragment를 추가한다.
이때 fragment의 xml 파일, kotlin 파일이 각각 존재해야 한다.
- id: fragment를 구분할 아이디
- name: fragment의 kotlin 파일 전체 경로
- label: 화면의 이름 (navigation bar로 표시할 경우 이 label이 표시됨)
- layout: fragment에 해당하는 xml
그리고 가장 루트의 <navigation> 태그 안에 startDestination 속성이 존재한다.
여기에는 nativation의 화면을 띄울 때 가장 먼저 보여야 할 fragment의 id를 넣어준다.
명시적으로 지정하지 않으면 오류가 발생한다.
Activity에 NavHost 추가
navigation을 띄울 Activity에 NavHost를 추가해보자.
NavHost는 사용자가 Navigation을 통해 화면을 전환할 때마다 빈 컨테이너를 통해 화면을 교체하는 역할을 한다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">
<fragment
android:id="@+id/nav_host_fragment_activity_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:navGraph="@navigation/nav_graph" />
<com.google.android.material.bottomnavigation.BottomNavigationView ... />
</androidx.constraintlayout.widget.ConstraintLayout>
navigation을 띄울 Activity에 위와 같은 fragment를 추가한다. (일반적으로 main Activity)
- name: NavHost의 클래스 이름을 지정한다.
- navGraph: 아까 생성해두었던 Navigation Graph xml 파일명을 입력한다. NavHost를 Navigation Graph와 연결한다.
- defaultNavHost: true로 지정하면 NavHostFragment가 뒤로 가기 버튼의 액션을 가져간다 (뒤로 가기 버튼을 누르면 이전에 머물렀던 Navigation의 Fragment로 이동) 만약 NavHost가 여러개인 경우, 하나의 NavHost만 이 속성을 지정할 수 있다.
Navigation Bar 구현하기
지금까지는 Navigation Graph를 생성해서 Navigation 안에 어떤 Fragment가 포함될 것인지 지정하고,
NavHost를 생성해서 화면을 보여줄 빈 컨테이너를 생성했다.
이제 이 Fragment를 전환하기 위한 버튼이 포함된 Navigation Bar를 Activity에 추가해야 한다.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/navigation_home"
android:icon="@drawable/ic_home_black_24dp"
android:title="@string/title_home" />
<item
android:id="@+id/navigation_dashboard"
android:icon="@drawable/ic_dashboard_black_24dp"
android:title="@string/title_dashboard" />
<item
android:id="@+id/navigation_notifications"
android:icon="@drawable/ic_notifications_black_24dp"
android:title="@string/title_notifications" />
</menu>
먼저 Navigation Bar에 들어갈 Fragment의 버튼들을 menu 형태로 생성한다.
bottom_nav_menu.xml라는 파일을 생성해서 Navigation Fragment 순서대로 item을 지정해주었다.
여기서 Activity에 보여질 icon과 title을 지정해준다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="@menu/bottom_nav_menu" />
<fragment ... />
</androidx.constraintlayout.widget.ConstraintLayout>
NavHost를 지정해둔 동일한 Activity에 BottomNavigationView를 추가한다.
menu 속성에 방금 만들어둔 bottom_nav_menu를 지정한다.
이제 Navigation을 위한 ui 작업은 모두 끝났다.
마지막으로 Navigation을 띄울 Activity kotlin 파일에서 NavController를 연결하는 작업이 필요하다.
NavController 연결하기
val navView: BottomNavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_activity_main)
navView.setupWithNavController(navController)
- Fragment.findNavController()
- View.findNavController()
- Activity.findNavController(viewId: Int)
NavController는 위와 같은 함수들을 통해 얻을 수 있다.
여기서 viewId는 xml 파일에서 NavHost View의 id가 된다.
그리고 Navigation Bar에 setupWithNavContoller()를 호출해서
NavController를 지정해주면 Navigation Bar에 대한 작업은 마무리된다.
+ Safe Args
Safe Args를 사용하여 Fragment 간에 Type Safe하게 데이터를 전달할 수 있다.
// module 수준
plugins {
id("androidx.navigation.safeargs.kotlin")
}
// project 수준
buildscript {
dependencies {
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.7.4")
}
}
Safe Args를 사용하기 위해서는 module, project 수준의 build.gradle에 각각 의존성을 추가한다.
override fun onClick(view: View) {
val action =
SpecifyAmountFragmentDirections
.actionSpecifyAmountFragmentToConfirmationFragment()
view.findNavController().navigate(action)
}
Safe Args는 각 화면마다 navigate할 수 있는 클래스를 생성한다.
발신 대상이 SpecifyAmountFragment라고 할 때, 이 클래스의 이름은 SpecifyAmountFragmentDirections이 된다.
이 클래스는 navigate()의 인자로 전달할 수 있는 NavDirections 객체를 반환한다.
발신 대상이 SpecifyAmountFragment, 수신 대상이 ConfirmationFragment라고 할 때,
actionSpecifyAmountFragmentTo ConfirmationFragment()라는 메서드를 통해 발신 대상에서 수신 대상으로 이동할 수 있는 action을 반환한다.
마무리
Navigation Component를 사용하지 않고 Toolbar를 생성하듯이 menu로 Navigation을 생성해줄 수도 있다.
나한테는 그 방법이 더 익숙했는데, onItemSelected()를 각 Fragment마다 구현해주어야 하기 때문에 작성해야 할 코드가 훨씬 많아진다.
아직 Navigation Component의 action, Safe Args 등등.. 와닿지 않는 개념들이 있지만 여러번 학습해보면서 익혀가야겠다.
참고
https://developer.android.com/guide/navigation?hl=ko
https://developer.android.com/guide/navigation/navigation-getting-started?hl=ko
'android' 카테고리의 다른 글
[안드로이드/코틀린] Dagger의 Hilt 라이브러리로 의존성 주입 (0) | 2023.12.03 |
---|---|
[안드로이드/코틀린] BottomNavigationView 커스텀 (1) | 2023.10.25 |
[안드로이드/코틀린] FCM를 이용하여 사용자 별로 Notification 보내기-2 (0) | 2023.10.16 |
[안드로이드/코틀린] FCM를 이용하여 사용자 별로 Notification 보내기-1 (0) | 2023.10.16 |
[안드로이드/코틀린] Retrofit2를 통한 PUT/DELETE 사용 예시 (0) | 2023.10.12 |