Android Navigation Component 튜토리얼 — 1

Harry The Great
해리의 유목코딩
12 min readDec 23, 2019

Jetpack과 함께 소개된 안드로이드 내비게이션은 기존 정말 복잡하고 난해하고 코드를 스파게티로 만들어줬던 Fragment와 Activity 간 구현을 심플하고 안정적으로 이동할 수 있도록 도와주는 컴포넌트입니다. 특히 가장 큰 이점은 Fragment 단위로 각각 원하는 내비게이션에 맞추어 테스트할 수 있습니다.

먼저 프로젝트를 만들어보며 소개하겠습니다. 현재 제가 사용하고 있는 버전은 Android Studio 3.6 Preview Version입니다. 소스코드는 최하단에 링크되어있습니다.

프로젝트 세팅하기

Empty Activity를 선택해준 후 minSDK는 23로 프로젝트를 생성합니다.

Project단위 Gradle

Safe-Args는 안드로이드 내비게이션 간 안정적인 매개변수 사용을 위해 쓰이는 라이브러리로 Bundle 인스턴스를 통해 파라미터를 전달했던 과정을 쉽게 처리할 수 있게 해 줍니다. 자세한 과정은 실제 내비게이션을 통해 설명하겠습니다.

App단위의 Gradle

apply plugin: "androidx.navigation.safeargs.kotlin"

앱 모듈의 상단에 safeargs가 적용될 수 있도록 선언해줍니다. 다음으로 필요한 디펜던시를 선언해줍니다. 코틀린이 아닌 자바에서는 하단 링크를 확인해주세요.

Navigation 디펜던시 Fragment 디펜던시

Fragment 생성하기

실습을 위해 사용될 Fragment를 생성할 차례입니다. 각각의 Fragment는 FirstHomeScreen, SecondHomeScreen, ThirdHomeScreen 3개의 Fragment를 생성하였고 각각 First, Second, Third 부분만 변경될 뿐 나머지는 모두 동일합니다.

이제 1개의 MainActivity와 3개의 Fragment가 생겼습니다. 이제 네비게이션을 구현해보겠습니다.

fragment 화면 예시

Activity Navigation Controller 구현하기

Navigation Controller를 구현하는 데는 여러 방법이 있지만 튜토리얼이기 때문에 가장 간단한 방법으로 해보겠습니다. activity_main.xml을 아래와 같이 수정합니다.

android:name="androidx.navigation.fragment.NavHostFragment"

android:name은 요소에 어떤 NavHost가 구현될지를 정의해주며 위에서는 Fragment NavHost임을 정의해주었습니다. 다음으로 navGraph는 우리가 사용할 내비게이션 리소스입니다. 마지막으로 defaultNavHost를 true로 설정하면 현재 구현되는 Activity에서의 뒤로 가기 버튼이 우리가 구현한 Navigation에 맞게 동작합니다.

만약 동일한 영역에 복수개의 Navigation Graph를 구현한다면 FrameLayout 혹은 FragmentContainerView를 사용할 수 있습니다.

Navigation 리소스 만들기

resources에 navigation 폴더를 만들고 그 안에 nav_main.xml을 생성해줍니다. 맨 처음 만들고 디자인 뷰로보면 아무것도 구현되어있지않은 맨화면을 볼 수 있습니다.

new destination버튼을 눌러 screen_first_home을 추가해줍니다.

맨 처음 화면을 추가하게 되면 위와 같이 home 아이콘이 함께 있습니다. startDestination을 뜻하며 상단 메뉴 막대들 중 동일한 home 아이콘을 사용해서 변경할 수 있습니다. 이제 직접 실행해보겠습니다.

구현모습

맨 화면에 덩그러니 fragment 하나만 구현되어있습니다. MainActivity에 코드 한 줄 추가하지 않고도 xml 선언만으로 fragment를 위와 같이 구현할 수 있습니다.

BottomNavigation 구현하기

조금 전 만들었던 navigation으로 돌아가 보겠습니다.

첫 번째 Fragment를 추가했던 방법과 동일하게 secondHomeScreen, thirdHomeScreen을 추가해줍니다. 이제 동일한 화면 영역에 더 많은 Fragment들을 구현할 수 있습니다. activity_main.xml로 이동합니다.

BottomNavigation을 추가해줍니다. 이제 위에 선언한 menu_bottom_navigation 만들어보겠습니다. 리소스에 menu 디렉터리를 만들고 nav_main을 생성합니다.

아이콘은 임의로 선언하였고 없어도 무방합니다. 각각 첫 번째 메뉴, 두 번째 메뉴, 세 번째 메뉴로 title값을 넣어주었습니다. 다음으로 id값들을 각각 구현하려 하는 Fragment들의 id값을 넣어주어야 하는데 navigation화면에서의 값들을 기준으로 합니다.

예를 들어 세 번째 메뉴를 위한 item이라면 nav_main.xml에 있는 id값과 동일하게 맞추어줍니다.

Acitivity 세팅

NavigationUI.setupWithNavController(main_bottom_navigation, findNavController(R.id.nav_host))

setContentView 하단에 위와 같이 선언해줍니다. NavigationUI.setupWithNavController은 view와 navController를 인자로 받아 view를 navController에 맞게 구현해줍니다. 이 과정에서 bottomNavigation의 menu값의 id와 fragment 목적지가 자동으로 맵핑됩니다.

NavController란?
NavController는 우리가 만든 내비게이션 그래에 맞게 NavHost 안이 구현되도록 도와주는 오브젝트입니다.

이제 앱을 실행해보면 하단 메뉴의 버튼에 맞추어 화면이 맵핑됩니다.

Action과 Argument

다시 Navigation 화면으로 돌아와 Fragment를 하나 클릭해보면 위와 같이 Arguments, Actions, Deep Links가 있습니다.

Argument

말 그대로 각각의 화면에서 인자로 받는 값입니다.

Array와 Optional(Nullable) 타입이 가능할 뿐만 아니라 enum, Parceble 값도 가능하고 기본값도 설정할 수 있기 때문에 이전처럼 Bundle에 putString, putInt을 할 필요가 없습니다. 만약 스택에 쌓였던 이전 Fragment들이 메모리에서 정리된다면 인자로 받은 Argument를 기준으로 다시 생성됩니다.

Actions

네비게이션이 어떻게 움직일지에 대한 Action값입니다.

화면을 하나 선택한 후 오른쪽 클릭을 하면 Add Action이라는 메뉴가 보입니다. 액션은 상당히 다양하게 정의할 수 있습니다.

  • To Destination : 현재 화면에서 다른 화면으로 이동하는 액션
  • To Self : 자기 자신으로 이동하는 액션
  • Return To Source: popBackStack시 사용될 액션
  • Global: 어떤 화면에서든 현재 화면으로 이동할때의 액션

다른화면으로의 이동은 조금 더 쉽게도 구현할 수 있습니다.

액션 화살표를 클릭하면 오른쪽에 Argument Default Values, Pop Behavior, launchSingleTop 메뉴를 볼 수 있습니다.

  • Argument Default Values: 만약 목적지에 Argument들이 있다면 해당 액션에서 각각의 Argument들에 대한 기본값을 입력해줄 수 있습니다.
  • Pop Behavior — popUpToIncursive : 화면 간 이동시 쌓여있는 Fragment 스택들에 대해 어떻게 처리할지를 정의합니다. 예를 들어 [스플래시] -> [로그인] -> [메인] 화면이 있다고 가정하고 로그인 화면에서 메인화면으로 가는 액션에 popUpToIncursive 옵션을 false로 주면 메인에서 Back button을 눌렀을 때 로그인 화면으로 돌아오게 됩니다. 하지만 true를 주면 로그인 화면은 스택에서 사라지고 C화면에서 Back버튼을 눌렀을 때 스플래시로 이동합니다.
  • Pop Behavior- popUpTo 옵션은 선택한 Fragment까지 쌓여있는 모든 스택을 popup 시킵니다. 만약 [스플래시] -> [로그인] -> [메인화면] 화면에서 popupTo값으로 메인화면을 준다면 쌓여있던 스플래시와 로그인 화면은 정리되고 메인화면만 남게 됩니다.

Navigation 이동하기

이제 이론을 알았으니 실제 이동해보기 위해 새로운 Fragment를 만들겠습니다. 많은 부분이 SafeArgs 라이브러리가 해주기 때문에 많은 양의 코드들이 자동으로 만들어집니다. 먼저 TwoDepthScreen을 Fragment를 만듭니다.

네비게이션에 추가하기

이전과 동일한 방법으로 nav_main.xml에 화면을 추가하고 클릭합니다.

arguments에 +버튼을 눌러 위와 같이 param_count라는 이름의 정수 값 인자를 만듭니다.

드래그를 하여 액션을 만들어줍니다. 이렇게 만들어진 액션은 FistHome에서 TwoDepth로 이동할 때 사용됩니다.

다음으로 twoDepthScreen에 오른쪽 버튼을 눌러 AddAction -> Global을 클릭하여 전역 액션도 만들어줍니다. 전역 액션은 특정한 출발지 이외에 모든 곳에서 접근할 수 있도록 해줍니다.

이제 TwoDepthScreen으로 이동할 수 있는 두 개의 액션이 생겼습니다.

이렇게 자동으로 액션을 이으면 액션을 클릭했을 때 id값으로 action_[출발지 이름]_to_[목적지 이름] 형식으로 id값이 자동으로 만들어집니다. 네이밍 때문에 헷갈릴 수 있어 수정은 하지 않겠습니다. 화면에서 끝의 2는 제가 중간에 지웠다 다시 만들어 생성되어 그렇습니다.

safeArgs는 우리가 xml에서 선언한 많은 양의 Argument와 Action들을 클래스와 메서드로 빌드 과정에서 만들어줍니다. 프로젝트 빌드를 먼저 해주세요.

우리가 만든 액션들을 기반으로 클래스들이 생성되는데 ID값을 수정하지 않는다면 [목적지 이름] + Directions이라는 정적 클래스와 목적지로 연결된 메서드들로 이루어집니다.

FirstHomeScreen -> TwoDepth

FirstHomeScreen에서 출발하기 때문에 FirstHomeScreenDirection이라는 클래스가 만들어지고 FirstHome에서 to TwoDepth로 이동시킬 수 있는 actionFirstHomeScreenToTwoDepth라는 메서드가 생기며 정수(paramCount)를 인자로 받는 메서드가 있고 이 메서드는 NavDirection를 리턴합니다.
NavDirection이란 NavController에게 어디로 이동을 해달라고 알려주는 매개변수입니다. NavController의 navigate 메서드에 내가 이동할 NavDirection을 주면 NavController는 TwoDepth로 이동시켜주고 Back Button을 눌렀을 때 popup behavior에 맞추어 이동시켜줍니다.

TwoDepth — Global

Global은 출발지 지점이 특정 Fragment가 아니기 때문에 nav_main.xml의 파스칼 케이스인 NavMainDirection이 생성되고 다시 actionGlobalTwoDepthScreen 메서드가 생성됩니다. SecondHomeScreen에서는 Global 액션으로 이동할 수 있도록 아래와 같이 구현합니다.

우리가 사용한 클래스들과 메서드는 빌드 과정에서 생성된 generated 디렉터리에서 확인할 수 있습니다.

Argument 받기

Fragment에서는 우리가 정의한 Argument들은 by navArgs<[Navigation ID]Args>로 받을 수 있습니다.

--

--

Harry The Great
해리의 유목코딩

Android & IOS Developer 😀 미디움 이외에 스니펫이나 디버그노트로 활용하는 https://www.harrymikoshi.com/ 블로그도 운영하고있습니다.