서론
Android에서 자주 사용되는 Jetpack Navigation에 대해 정리해보려 한다. 기존 화면 전환 시 intent, transaction을 사용하지만, 앱 구성이 복잡해질수록 코드도 복잡해질 수 있다. 화면 구성 및 UI 전환을 쉽게 구현할 수 있도록 도와주고, SAA 아키텍처에서 함께 사용되는 개념이라 기본적인 내용이지만 중요하다고 생각한다.
Navigation이란?
먼저, Jetpack이란 Android 개발을 쉽고 빠르게 도와주는 라이브러리이다.
Navigation은 UI(xml)을 통해 navigation 편집이 가능하게 해주는 라이브러리로 주로 BottomNavigation과 자주 사용된다. Fragment의 전환을 좀 더 간단하게 도와주는 도구로 생각하는 게 이해하기 가장 쉬울 것 같다.
주요 구성 요소
- Host : 현재 네비게이션이 포함된 UI 요소 즉, 사용자가 앱을 탐색할 때 앱은 기본적으로 네비게이션 호스트 안팎으로 대상을 전환한다. - NavHostFragment(프래그먼트)
- Graph : 앱 내의 모든 탐색 대상과 연결 방법을 정의하는 데이터 구조 - NavGraph
- Controller : 대상 간 탐색을 관리하는 역할, 컨트롤러는 대상 간 탐색, 딥 링크 처리, 백 스탭 관리 등의 작업을 위한 메서드를 제공한다. - NavController
장점
- 애니메이션 및 전환 : 애니메이션 및 전환에 표준화된 리소스 제공
- 딥 링크 : 사용자를 대상으로 직접 연결하는 딥 링크를 구현하고 처리함
- UI 패턴 : 최소한의 추가 작업으로 탐색창 및 하단 탐색과 같은 패턴 지원
- 유형 안전성 : 대상 간에 데이터를 탐색하고 전달할 때 유형 안전성을 제공하는 Safe Args 플러그인이 포함됨
- ViewModel 지원 : ViewModel의 범위를 탐색 그래프로 지정하여 그래프 대상 간에 UI 관련 데이터를 공유할 수 있음
- 프래그먼트 트랜잭션 : 프래그먼트 트랜잭션을 완전히 지원하고 처리함
- 이동 처리 : 기본적으로 뒤로 및 위로 작업을 올바르게 처리함
구현
kotlin 구현 예시이므로, 참고 부탁드립니다.
build.gradle(app)
// jetpack navigation
def nav_version = "2.5.3"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
res - navigation 패키지 생성 후 navigation_graph.xml 파일 생성
작성 코드는 IntroFramgnet -> HomeFragment -> WebViewFragment로 작성한 예시이고,
보통 앱의 Intro 화면의 경우 실행 시 한번만 노출되고 다시 노출되지 않기 때문에 스택 제거까지 포함된 예시입니다.
<?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/navigation_graph"
app:startDestination="@id/introFragment">
<fragment
android:id="@+id/introFragment"
android:name="com.soo.sample_saa_project.view.fragment.IntroFragment"
android:label="IntroFragment"
tools:layout="@layout/fragment_intro"
>
<action
android:id="@+id/action_introFragment_to_homeFragment"
app:destination="@id/homeFragment"
app:enterAnim="@anim/fade_in"
app:exitAnim="@anim/fade_out"
app:popEnterAnim="@anim/fade_in"
app:popExitAnim="@anim/fade_out"
app:popUpTo="@id/introFragment"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="@+id/homeFragment"
android:name="com.soo.sample_saa_project.view.fragment.HomeFragment"
android:label="HomeFragment"
tools:layout="@layout/fragment_home"
>
<action
android:id="@+id/action_homeFragment_to_webViewFragment"
app:destination="@id/webViewFragment"
app:enterAnim="@anim/fade_in"
app:exitAnim="@anim/fade_out"
app:popEnterAnim="@anim/fade_in"
app:popExitAnim="@anim/fade_out" />
</fragment>
<fragment
android:id="@+id/webViewFragment"
android:name="com.soo.sample_saa_project.view.fragment.WebViewFragment"
android:label="WebViewFragment"
tools:layout="@layout/fragment_webview"
>
<argument
android:name="url"
app:argType="string"
android:defaultValue=""
/>
</fragment>
</navigation>
- startDestination - 네비게이션 시작점
- tools:layout - 위 요소 추가 시 design 화면에서 preview 확인 가능
- action - 화살표로 화면 간의 이동을 나타냄
- argument - 데이터 전달 시 사용됨
- enterAnim, exitAnim, popEnterAnim, popExitAnim - 화면 전환 시의 애니메이션
- destination - 목적지 설정
- popUpTo - 백스택에서 어디까지 이동할 것인지 결정하는 속성
- popUpToInclusive - popUpto로 지정한 fragment까지 pop 시킬 것인지 정함
즉, IntroFragment에서 HomeFragment로 이동 정의 후 popUpTo를 IntroFragment로 설정하고 Inclusvie를 true로 설정해 두었기 때문에 IntroFragment로 돌아가지 않음(스택에서 제거)
위와 같이 설정해두면 네비게이션은 아래와 같이 설정됩니다. (물론 각 Framgnet는 미리 생성해두어야 함, preview도 fragment에 설정된 ui로 노출됨)
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout
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"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/navigation"
app:defaultNavHost="true"
/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/navigation"
android:layout_width="match_parent"
android:layout_height="48dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:menu="@menu/navigation_item"
app:labelVisibilityMode="unlabeled"
android:background="@color/black"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
FragmentContainerView에 전환된 Fragment가 노출되고, BottomNavigationView를 통해 하단 네비게이션 버튼이 노출됩니다.
MainActivity
private lateinit var navController: NavController
private lateinit var navHostFragment: NavHostFragment
navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
binding.navigation.setupWithNavController(navController)
navController.setGraph(R.navigation.navigation_graph)
위와 같이 네비게이션을 연결해 줍니다.
화면 전환 예시(버튼 클릭 시 화면 전환)
val action = HomeFragmentDirections.actionHomeFragmentToWebViewFragment(url) // webfragment로 이동 및 url 전달
findNavController().navigate(action)
기존 navigation_graph.xml 에 정의해 둔 action을 가져와 navigate 해주면 화면 이동을 구현할 수 있습니다.
마치며
오늘은 간단하게 네비게이션에 대해 정리를 해보았다. 실제 프로젝트에 적용하기 위해서는 위 내용보다 좀 더 세부적인 내용들에 대한 학습이 필요하다. 예를 들면 데이터 전달을 위한 safe Args라던가, 백스택 관리에 좀 더 신경 써야 할 것 같다. 다음 포스팅 때는 기존 화면전환 때 Intent, Bundle 등을 활용했다면 네비게이션에는 Safe Args가 있기 때문에 해당 내용을 정리해보려 한다.
위 소스에 대한 전체 코드는 아래 깃허브에 정리해 두었습니다😉
https://github.com/pparksoo11/Single_Activity_Architecture_Sample
GitHub - pparksoo11/Single_Activity_Architecture_Sample: SAA + 클린 아키텍처 + MVVM + Hilt 등을 활용한 예제
SAA + 클린 아키텍처 + MVVM + Hilt 등을 활용한 예제. Contribute to pparksoo11/Single_Activity_Architecture_Sample development by creating an account on GitHub.
github.com
Reference
https://developer.android.com/guide/navigation?authuser=2&hl=ko
탐색 | Android 개발자 | Android Developers
Android Jetpack의 탐색 구성요소를 사용하여 앱에서 탐색 구현
developer.android.com