Search code examples
androidandroid-lifecycleandroid-architecture-navigation

How to achieve a modern Android Navigation implementation


I would like to implement a navigation logic based in the Android Navigation Component in it's more updated version (2.4.0-beta02) but I'm facing some kind of issues (or non expected behaviors) related with the Android lifeycle.

My main problem is that each time you change a tab, fragments are totally recreated which means that onCreate() method it's called every time the tab is changed. I understand that for performance reasons, fragments call onDestroyView() method each time you left the tab associated with it and then when you came back to that tab onCreateView() method will be called. Actually I don't know if my implementation of the Navigation Component and it's behavior it's correct or not and I would like to know how correctly implement it.

My actual implementation looks like this:

Gradle file:

def navigation = "2.4.0-beta02"
implementation "androidx.navigation:navigation-runtime-ktx:$navigation"
implementation "androidx.navigation:navigation-fragment-ktx:$navigation"
implementation "androidx.navigation:navigation-fragment-ktx:$navigation"
implementation "androidx.navigation:navigation-ui-ktx:$navigation"

The activity that holds my tabs:

class MainActivity : AppCompatActivity() {

  private lateinit var binding: ActivityMainBinding
  private lateinit var navController: NavController

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)

    val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_container) as NavHostFragment
    navController = navHostFragment.navController

    binding.bottomNav.setupWithNavController(navController)
  }

}

The activity layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

          <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_container"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
        android:name="androidx.navigation.fragment.NavHostFragment"
            app:navGraph="@navigation/nav_graph"
            app:defaultNavHost="true" />
 
         <com.google.android.material.
            bottomnavigation.BottomNavigationView
            android:id="@+id/bottom_nav"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginStart="0dp"
            android:layout_marginEnd="0dp"
            android:background="@android:color/white"
            app:itemIconTint="@color/gray"
            app:itemRippleColor="@android:color/transparent"
            app:itemTextColor="@color/gray"
            app:labelVisibilityMode="unlabeled"
            app:menu="@menu/bottom_nav_menu" />
    
</LinearLayout>

Graph:

<navigation
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:id="@+id/nav_graph"
  app:startDestination="@+id/home">

  <include app:graph="@navigation/home"/>
  <include app:graph="@navigation/profile"/>

</navigation>

Navigation Home:

 <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/home"
  app:startDestination="@+id/homeFragmentNavigation">

  <fragment
      android:id="@+id/homeFragmentNavigation"
android:name="com.tayloring.android.view.page.tabs.HomeFragment"
      android:label="@string/title_home"
      tools:layout="@layout/fragment_home"/>
  </fragment>

</navigation>

Secondary Navigation:

  <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/profile"
    app:startDestination="@+id/profileFragmentNavigation">

    <fragment
      android:id="@+id/profileFragmentNavigation"      
      android:name="com.tayloring.android.view
      .page.user.ProfileFragment"
      android:label="@string/title_profile"
      tools:layout="@layout/fragment_profile"
    </fragment>

  </navigation>

Secondary fragment:

class ProfileFragment : Fragment() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        println("I'M ALWAYS CALLED")
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_profile, container, false)
    }
}

Also I tried this repository https://github.com/android/architecture-components-samples/tree/master/NavigationAdvancedSample which it's the example that some moderns posts of medium talks about when they are talking about the Navigation Component, but it is outdated and it's Navigation version is older.


Solution

  • The Navigation Component calls onDestroy() method each time a tab it's changed so it's the normal behaviour.