이번 영상에서는 Jetpack Navigation을 이용해 BottomNavigationView를 구현하고 AppBar 타이틀을 연동하는 법에 대해 알아보도록 하겠습니다.
우선 Navigation Dependency를 추가합니다.
// Navigation
implementation 'androidx.navigation:navigation-fragment-ktx:2.4.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.4.2'
그럼 BottomNavigationView 화면을 작성하겠습니다. 우선은 res > navigation 폴더 아래에 booksearch_nav_graph라는 이름으로 내비게이션 그래프를 만들어 줍니다. Navigation Editor에서 프래그먼트를 추가하고 id와 label을 설정해줍니다. 이 때 id는 기존의 menu.xml에 지정한 id와 동일한 값이어야 합니다. 화면의 시작은 SearchFragment가 되도록 설정했습니다.
<?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/booksearch_nav_graph"
app:startDestination="@id/fragment_search">
<fragment
android:id="@+id/fragment_favorite"
android:name="com.example.booksearchapp.ui.view.FavoriteFragment"
android:label="@string/favorite"
tools:layout="@layout/fragment_favorite" />
<fragment
android:id="@+id/fragment_search"
android:name="com.example.booksearchapp.ui.view.SearchFragment"
android:label="@string/search"
tools:layout="@layout/fragment_search" />
<fragment
android:id="@+id/fragment_settings"
android:name="com.example.booksearchapp.ui.view.SettingsFragment"
android:label="@string/settings"
tools:layout="@layout/fragment_settings" />
</navigation>
다음은 fragment의 네비게이션을 수행할 FragmentContainerView를 FrameLayout 내부에 추가합니다.
<FrameLayout
android:id="@+id/frame_layout"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/bottom_navigation_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/booksearch_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/booksearch_nav_graph" />
</FrameLayout>
그리고 MainActivity의 setupJetpackNavigation에서 내비게이션을 설정합니다. 우선 네비게이션 컨트롤러 인스턴스를 취득하고 setupWithNavController를 써서 bottomNavigationView를 Navigation controller와 연결해주면 Navigation이 프래그먼트 전환을 수행해주게 됩니다. 기존의 setupBottomNavigationView는 필요가 없으니 삭제합니다.
class MainActivity : AppCompatActivity() {
...
+ private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
- setupNavigation()
- if (savedInstanceState == null) {
- binding.bottomNavigationView.selectedItemId = R.id.search_fragment
- }
+ setupJetpackNavigation()
...
}
+ private fun setupJetpackNavigation() {
+ val host = supportFragmentManager
+ .findFragmentById(R.id.booksearch_nav_host_fragment) as NavHostFragment? ?: return
+ navController = host.navController
+ binding.bottomNavigationView.setupWithNavController(navController)
+ }
}
이 때 AppBar의 타이틀을 Navigation graph에 정의된 값으로 표시할 수도 있습니다.
+ private lateinit var appBarConfiguration: AppBarConfiguration
private fun setJetpackNavigation() {
...
+ appBarConfiguration = AppBarConfiguration(
+ navController.graph
+ )
+ setupActionBarWithNavController(navController, appBarConfiguration)
}
+ override fun onSupportNavigateUp(): Boolean {
+ return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
+ }
AppBarConfiguration 에 Navigation graph를 넘겨주는데, 이 때 navController.graph를 넘겨주면 현재 Navigation graph의 계층구조에 따라 Search 프래그먼트가 Top-level destination으로 지정됩니다. 그리고 [setupActionBarWithNavController(https://developer.android.com/reference/androidx/navigation/ui/NavigationUI#setupActionBarWithNavController(androidx.appcompat.app.AppCompatActivity,androidx.navigation.NavController,androidx.customview.widget.Openable))로 Navigation controller와 연결해주면 됩니다.
실행을 시켜보면 현재 표시되는 프래그먼트가 Top-level destination이 아니라면 AppBar에 뒤로가기<- 버튼도 같이 표시됩니다. 이 버튼을 작동시키기 위해서는 onSupportNavigateUp을 다음과 같이 오버라이드 해줍니다. navController와 appBarConfiguration은 전역변수로 변경시켜주어야겠지요.
여기서는 모든 프래그먼트를 Top-level destination으로 만들고 싶은데요, 그럴려면 setOf로 프래그먼트의 리스트를 넘겨주면 됩니다.
private fun setJetpackNavigation() {
val host = supportFragmentManager
.findFragmentById(R.id.booksearch_nav_host_fragment) as NavHostFragment? ?: return
navController = host.navController
binding.bottomNavigationView.setupWithNavController(navController)
appBarConfiguration = AppBarConfiguration(
- navController.graph
+ setOf(
+ R.id.fragment_search, R.id.fragment_favorite, R.id.fragment_settings
+ )
)
setupActionBarWithNavController(navController, appBarConfiguration)
}