Komponen Navigasi-Jutsu, vol. 1 - BottomNavigationView



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


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 FragmentManager โ€“ FragmentManager 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