Jetpack Navigation-3 고급 사용법
Jetpack Navigation-1 기초 및 구성 요소
Jetpack Navigation-2 기초 사용법
Jetpack Navigation-3 고급 사용법 ← Here
NavigationUI
내비게이션 라이브러리에는 NavigationUI
클래스가 포함되어 있습니다. NavigationUI
는 menu item
과 option menu
, app bar
, navigation drawer
, bottom navigation
등 UI 컴포넌트와 내비게이션 그래프를 연결하는 메서드를 가지고 있습니다.
NavigationUI
는 menu item
의 id와 내비게이션 그래프의 destination
id가 동일한 경우 해당 menu item
과 destination
을 연결합니다.
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
기존의 방식은 BottomNavigationView
의 setOnNavigationItemSelectedListener
를 사용하여 선택하는 아이템에 따라 수행할 동작을 작성해야 합니다.
NavigationView
와 마찬가지로 BottomNavigationView
의 setupWithNavController()
를 호출하여 설정을 마무리합니다.
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
처럼 단순히 몇 가지 결과를 전달하는 데 뷰 모델을 생성하고 관리하는것은 상당히 비효율적으로 느낄 수 있습니다.
위의 상황에서 NavController
의 NavBackStackEntry
를 사용하면 문제를 쉽게 해결할 수 있습니다. NavBackStackEntry
는 destination에 대해 NavController
관련 상태를 캡슐화합니다.
Frag1이 Frag2로부터 결과를 얻는 과정은 다음과 같습니다.
- Frag2에서
previousBackStackEntry
를 참조하여savedStateHandle
에 key-value 데이터를 저장. - Frag2를 pop
- 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