
Vor zwei Jahren wurde Android-Entwicklern bei Google I / O eine neue Lösung für die Navigation in Anwendungen vorgestellt - die Bibliothek Jetpack Navigation Component. Über kleine Anwendungen wurde bereits genug gesagt, aber es gibt Navigation Componentpraktisch keine Informationen darüber, auf welche Probleme Sie beim Übersetzen einer großen Anwendung stoßen können .
, , 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, , .