
Dua tahun lalu di Google I / O, pengembang Android disajikan dengan solusi baru untuk navigasi dalam aplikasi - perpustakaan Jetpack Navigation Component. Sudah cukup banyak yang dikatakan tentang aplikasi kecil, tetapi secara Navigation Componentpraktis tidak ada informasi tentang masalah apa yang mungkin Anda temui saat menerjemahkan aplikasi besar ke dalam .
, , Navigation Component Android-.
Android 11 Android Academy. , . โ .
, BottomNavigationView, โ , โ , , . โ , , , , .
Disclaimer
, hh.ru, , . , , , .
:

, , , .
BottomNavigationView
- Navigation Component, : BottomNavigationView Google back stack- . , , .
โ , . , , .

Android Studio 4.1 Beta ( - ) . .
-
Activity
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/container">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
app:menu="@menu/bottom_nav_menu" />
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
ยซยป , .
ConstraintLayout, BottomNavigationView <fragment> NavHostFragment- (Android Studio, , , FragmentContainerView).
-
BottomNavigationView
<navigation
android:id="@+id/mobile_navigation"
app:startDestination="@+id/navigation_home">
<fragment
android:id="@+id/navigation_home"
android:name="com.aaglobal.graph_example.ui.home.HomeFragment"/>
<fragment
android:id="@+id/navigation_dashboard"
android:name="com.aaglobal.graph_example.ui.dashboard.DashboardFragment"/>
<fragment
android:id="@+id/navigation_notifications"
android:name="com.aaglobal.graph_example.ui.notifications.NotificationsFragment"/>
</navigation>destination- .
- -
BottomNavigationView
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/navigation_home"
android:icon="@drawable/ic_home_black_24dp"
android:title="@string/title_home" />
<item
android:id="@+id/navigation_dashboard"
android:icon="@drawable/ic_dashboard_black_24dp"
android:title="@string/title_dashboard" />
<item
android:id="@+id/navigation_notifications"
android:icon="@drawable/ic_notifications_black_24dp"
android:title="@string/title_notifications" />
</menu> , destination- . BottomNavigationView , , .
.

Dashboard ViewModel . , Home Dashboard, . Home Dashboard. .
Issue Tracker-. , Google- Fragment-, back stack- FragmentManager-. Medium Ian Lake, , Google , , , BottomNavigationView .
โ . , BottomNavigationView, . , , .

Dashboard , . , Dashboard, , Graphic. Back โ , . , Graphic, Dashboard, , .
ยซ ยป, โ . .
workaround
Google- Architecture Components, NavigationAdvancedSample.
NavigationExtensions.kt. , , , .
- -, ,
<?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_home"
app:startDestination="@id/HomeFragment">
<fragment
android:id="@+id/HomeFragment"
android:name="com.aaglobal.jnc_playground.ui.home.HomeFragment"
android:label="@string/title_home"
tools:layout="@layout/fragment_home" />
</navigation>, BottomNavigationView XML, startDestination .
- -,
NavHostFragment,
private fun obtainNavHostFragment(
fragmentManager: FragmentManager,
fragmentTag: String,
navGraphId: Int,
containerId: Int
): NavHostFragment {
// If the Nav Host fragment exists, return it
val existingFragment = fragmentManager.findFragmentByTag(fragmentTag) as NavHostFragment?
existingFragment?.let { return it }
// Otherwise, create it and return it.
val navHostFragment = NavHostFragment.create(navGraphId)
fragmentManager.beginTransaction()
.add(containerId, navHostFragment, fragmentTag)
.commitNow()
return navHostFragment
}FragmentManager back stack- , , back stack. NavHostFragment- . , BottomNavigationView NavController.
- -,
BottomNavigationViewlistener, back stack-
setOnNavigationItemSelectedListener { item ->
val newlySelectedItemTag = graphIdToTagMap[item.itemId]
if (selectedItemTag != newlySelectedItemTag) {
fragmentManager.popBackStack(firstFragmentTag, FragmentManager.POP_BACK_STACK_INCLUSIVE)
val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag)
as NavHostFragment
if (firstFragmentTag != newlySelectedItemTag) {
fragmentManager.beginTransaction()
.attach(selectedFragment)
.setPrimaryNavigationFragment(selectedFragment).apply {
graphIdToTagMap.forEach { _, fragmentTagIter ->
if (fragmentTagIter != newlySelectedItemTag) {
detach(fragmentManager.findFragmentByTag(firstFragmentTag)!!)
}
}
}
.addToBackStack(firstFragmentTag)
.setReorderingAllowed(true)
.commit()
}
selectedNavController.value = selectedFragment.navController
true
} else {
false
}
} , BottomNavigationView FragmentManager-, . , back stack-.
-
BottomNavigationViewLiveData,NavController.NavController, ,ActionBar-
class RootActivity : AppCompatActivity(R.layout.activity_root) {
private var currentNavController: LiveData<NavController>? = null
private fun setupBottomNavigationBar() {
// Setup the bottom navigation view with a list of navigation graphs
val liveData = bottom_nav.setupWithNavController(
navGraphIds = listOf(
R.navigation.home_nav_graph,
R.navigation.dashboard_nav_graph,
R.navigation.notifications_nav_graph
),
fragmentManager = supportFragmentManager,
containerId = R.id.nav_host_container,
intent = intent
)
// Whenever the selected controller changes, setup the action bar.
liveData.observe(this, Observer { ctrl -> setupActionBarWithNavController(ctrl) })
currentNavController = liveData
}
} BottomNavigationView onCreate-, Activity , onRestoreInstanceState, Activity .
, , , BottomNavigationView, .

, , .
workaround- โ .
:

โ :

workaround-
, workaround , . NavigationAdvancedSample Activity, .
:

, Splash-:

Google , Splash- โ , UX . , Splash- โ Android-. Single Activity-, Fragment, Activity:

BottomNavigationView :
class MainFragment : Fragment(R.layout.fragment_main) {
private var currentNavController: LiveData<NavController>? = null
override fun onViewStateRestored(savedInstanceState: Bundle?) {
super.onViewStateRestored(savedInstanceState)
setupBottomNavigationBar()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (savedInstanceState == null) {
setupBottomNavigationBar()
}
}
} Splash- BottomNavigationView. hh.ru, ActionBar.
Theme.MaterialComponents.DayNight.DarkActionBar Theme.MaterialComponents.DayNight.NoActionBar NavController- ActionBar-:
class MainFragment : Fragment(R.layout.fragment_main) {
private var currentNavController: LiveData<NavController>? = null
private fun setupBottomNavigationBar() {
val navGraphIds = listOf(
R.navigation.search__nav_graph,
R.navigation.favorites__nav_graph,
R.navigation.responses__nav_graph,
R.navigation.profile__nav_graph
)
val controller = bottom_navigation.setupWithNavController(
navGraphIds = navGraphIds,
fragmentManager = requireActivity().supportFragmentManager,
containerId = R.id.fragment_main__nav_host_container,
intent = requireActivity().intent
)
currentNavController = controller
}
}
, , Splash- . .
? onDestroyView NavHostFragment NavController-. - NavController, LiveData, Navigation.findNavController onDestroyView .
NavController- ( Navigation Component- Navigation.setViewNavController), .
class MainFragment : Fragment(R.layout.fragment_main) {
private var currentNavController: LiveData<NavController>? = null
private fun setupBottomNavigationBar() {
...
currentNavController?.observe(
viewLifecycleOwner,
Observer { liveDataController ->
Navigation.setViewNavController(requireView(), liveDataController)
}
)
}
} . Don't keep activities, , . , โ IllegalStateException FragmentManager โ FragmentManager already executing transactions.

, , , .
, NavHostFragment FragmentManager- . : attach-detach Handler.post {}.
// NavigationExtensions.kt
private fun attachNavHostFragment(
fragmentManager: FragmentManager,
navHostFragment: NavHostFragment,
isPrimaryNavFragment: Boolean
) {
Handler().post {
fragmentManager.beginTransaction()
.attach(navHostFragment)
.apply {
if (isPrimaryNavFragment) {
setPrimaryNavigationFragment(navHostFragment)
}
}
.commitNow()
}
} Handler.post , .
BottomNavigationView
-
BottomNavigationViewNavigation Component, , workaround-. -
BottomNavigationView, , .