Componente de navegación-Jutsu, vol. 1 - BottomNavigationView



Hace dos años, en Google I / O, a los desarrolladores de Android se les presentó una nueva solución para la navegación en aplicaciones: la biblioteca Jetpack Navigation Component. Ya se ha dicho suficiente sobre las aplicaciones pequeñas, pero Navigation Componentprácticamente no hay información sobre los problemas que puede encontrar al traducir una aplicación grande a .



, , Navigation Component Android-.



Android 11 Android Academy. , . – .



, BottomNavigationView, – , – , , . — , , , , .



Disclaimer



, hh.ru, , . , , , .



:





, , , .



BottomNavigationView



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



— , . , , .



?





Android Studio 4.1 Beta ( - ) . .



  • Activity


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-
<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,


NavHostFragment- BottomNavigationView
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.



  • -, BottomNavigationView listener, back stack-


Listener BottomNavigationView
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-.



  • BottomNavigationView LiveData, NavController . NavController , , ActionBar-


BottomNavigationView Activity
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-:



BottomNavigationView
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
    }
}


Don't keep activities, … .



-



, , 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 FragmentManagerFragmentManager already executing transactions.



-



, , , .



, NavHostFragment FragmentManager- . : attach-detach Handler.post {}.



IllegalStateException
// 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



  • BottomNavigationView Navigation Component , , workaround-.
  • BottomNavigationView, , .


BottomNavigationView , .




All Articles