
Dois anos atrás, no Google I / O, os desenvolvedores Android foram apresentados a uma nova solução para aplicativos de navegação - a biblioteca Jetpack Navigation Component. Já foi dito o suficiente sobre pequenos aplicativos, mas Navigation Componentpraticamente não há informações sobre quais problemas você pode encontrar ao traduzir um grande aplicativo para o .
, , 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, , .