Jetpack Navigation-3 고급 사용법

seong-hwan Kim
shDev
Published in
7 min readJan 19, 2021

Jetpack Navigation-1 기초 및 구성 요소
Jetpack Navigation-2 기초 사용법
Jetpack Navigation-3 고급 사용법 ← Here

NavigationUI

내비게이션 라이브러리에는 NavigationUI 클래스가 포함되어 있습니다. NavigationUImenu itemoption menu, app bar, navigation drawer, bottom navigation 등 UI 컴포넌트와 내비게이션 그래프를 연결하는 메서드를 가지고 있습니다.

NavigationUImenu item의 id와 내비게이션 그래프의 destination id가 동일한 경우 해당 menu itemdestination을 연결합니다.

Options menu

안드로이드에서 앱 바에 메뉴 아이콘을 추가하기 위해선 메뉴를 inflate하고 onOptionsItemSelected() 메서드를 오버라이드하여 선택한 메뉴 id 마다 동작을 지정해줘야 했습니다.

NavigationUI는 opOptionsItemSelected 콜백을 간소화할 수 있습니다. 메뉴 아이템에 대해 onNavDestinationSelected() 확장 함수를 호출함으로 메뉴 리소스에 정의된 id와 동일한 id의 destination으로 이동할 수 있습니다.

Top app bar

NavigationUI는 화면 전환에 따라 앱 바의 제목을 자동으로 변경할 수 있습니다. 앱 바의 타이틀에는 내비게이션 그래프에서 destination의 label 속성에 지정된 값이 들어갑니다. label에 지정되는 값은 문자열 리터럴일 수도 있고 {argName} 처럼 argument의 값을 넣을 수도 있습니다.

NavigationUI는 다음과 같은 앱 바 유형을 지원합니다.

  • Toolbar
  • CollapsingToolbarLayout
  • ActionBar

AppBarConfiguration

AppBarConfiguration은 toolbar, collapsing toolbar, action bar 등의 옵션을 설정하는데 사용되며, 앱 바의 DrawerLayout 처리 여부와 top-level destination을 판별하는데 사용됩니다.

AppBarConfiguration 생성자의 첫 번째 파라미터로 내비게이션 그래프의 인스턴스 또는 top-level로 선언할 destination의 id set이 들어갈 수 있습니다. NavigationUI를 사용하면 파라미터에 지정한 top-level destination이 아닌 경우에만 앱 바에 Up 버튼을 표시합니다.

AppBarConfiguration 객체를 생성한 이후 툴 바의 setupWithNavController() 확장 함수를 호출하여 앱 바의 이벤트 처리와 옵션을 설정할 수 있습니다.

Note:
Toolbar를 사용하면 onSupportNavigationUp() 메서드를 오버라이드하지 않아도 됩니다.

Navigation drawer

내비게이션 컴포넌트 이전의 방식은 Navigation drawer를 제어하기 위해 OnNavigationItemSelectedListener를 추가하여 DrawerLayout의 제어와 선택된 아이템에 대한 동작을 작성해야 했습니다.

NavigationUI를 사용하면 위의 과정을 간소화 할 수 있습니다.

Navigation drawer를 적용하기 위해선 먼저 상단 앱 바와 연동하기 위해 drawerLayout을 추가하여 AppBarConfiguration 객체를 생성합니다.

이후 NavigationView 객체의 setupWithNavController() 확장 함수를 호출하여 설정을 마칩니다.

Bottom navigation

기존의 방식은 BottomNavigationViewsetOnNavigationItemSelectedListener를 사용하여 선택하는 아이템에 따라 수행할 동작을 작성해야 합니다.

NavigationView와 마찬가지로 BottomNavigationViewsetupWithNavController()를 호출하여 설정을 마무리합니다.

NavigationUI는 선택하는 메뉴에 따라 동일한 id의 destination으로 자동으로 이동하지만 변경에 대해 리스너를 추가할 수도 있습니다.

Navigation graph의 확장

Nested graph

루트 그래프 내에 또 다른 <navigation> 노드를 추가하여 내비게이션 흐름을 그룹화할 수 있습니다.

중첩 그래프는 destination을 캡슐화합니다. 루트 그래프와 마찬가지로 중첩 그래프에는 startDestination이 명시되어 있어야 하고 그래프 외부의 destination은 startDestination을 통해서만 중첩 그래프에 접근합니다.

XML 구성

위의 그림과 같이 내비게이션 그래프의 일부 흐름이 중첩 그래프로 캡슐화 된 상황에서 XML은 다음처럼 구성됩니다.

<include> 를 사용한 그래프 리소스의 분리

내비게이션 그래프에서 <include> 태그를 사용하여 다른 그래프를 참조할 수 있습니다. 기본적으로 nested graph와 동일한 역할을 수행하지만 <include> 태그를 사용하면 다른 프로젝트 모듈 또는 라이브러리 프로젝트의 그래프를 사용할 수 있습니다.

상호 작용

destination간의 이동에서, action에 인자를 추가하여 target destination으로 간단한 데이터를 넘길 수 있습니다. 하지만 대용량 데이터를 다뤄야 하거나, destination의 결과를 전달해야 한다면 shared viewmodel을 생성하고 두 destination이 뷰 모델을 observe 해야합니다.

하지만 뷰 모델을 생성할 때 LifecyckeOwner를 액티비티로 지정하여 생성한다면, 루트 액티비티의 생명 주기에 매치되기 때문에 결과적으로 뷰 모델이 앱의 생명 주기동안 유지된다는 단점이 있습니다.

Navigation Component는 이 문제에 대해 그래프의 생명 주기를 따르는 뷰 모델 생성을 지원합니다.

이로써 내비게이션 그래프에서 destination 사이의 데이터 공유와 result 처리가 가능하지만, startActivityForResult 처럼 단순히 몇 가지 결과를 전달하는 데 뷰 모델을 생성하고 관리하는것은 상당히 비효율적으로 느낄 수 있습니다.

위의 상황에서 NavControllerNavBackStackEntry를 사용하면 문제를 쉽게 해결할 수 있습니다. NavBackStackEntry는 destination에 대해 NavController 관련 상태를 캡슐화합니다.

Frag1이 Frag2로부터 결과를 얻는 과정은 다음과 같습니다.

  1. Frag2에서 previousBackStackEntry를 참조하여 savedStateHandle에 key-value 데이터를 저장.
  2. Frag2를 pop
  3. Frag1에서 currentBackStackEntry를 참조하여 key를 통해 savedStateHandle에 저장된 value에 접근.

Frag1에서는 savedStateHandle에 저장된 key를 LiveData로 관리하여 데이터가 변경됐을 때 옵저버를 설정할 수 있습니다.

참고

[Android Jetpack] AAC Navigation Component -3. NavigationUI & Animation/Transition
[Android Jetpack] AAC Navigation Component -4. Navigation Upgrade
Navigation Component에서 Result 전달하기
Codelab-Navigation

--

--