Currently I'm developing an Android app, in which I would like to utilize both Android Navigation Component
and BottomNavigationView
.
While working on said app I checked out the official codelab and several questions here, but they didn't prove to be of any help.
The problem is that assigning navController
to my BottomNavigationView
does not seem to have any effect - clicking on menu items does not affect the navigation host.
My code:
main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayoutxmlns: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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/main_host_fr"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/main_nav_bnv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@android:color/transparent"
android:elevation="0dp"
app:elevation="0dp"
app:menu="@menu/menu_main" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
MainActivity.kt
import android.os.Bundle
import android.os.PersistableBundle
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.NavigationUI
import androidx.navigation.ui.setupWithNavController
import com.aredruss.qurio.databinding.ActivityMainBinding
import com.aredruss.qurio.helpers.viewBinding
class MainActivity : AppCompatActivity() {
// ViewBindingDelegate
private val binding: ActivityMainBinding by viewBinding(ActivityMainBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
// Using onPostCreate because accessing NavHost in OnCreate causes crashes
override fun onPostCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onPostCreate(savedInstanceState, persistentState)
binding.navigationBb.setupWithNavController(findNavController(R.id.main_host_fr))
NavigationUI.setupWithNavController(binding.mainNavBnv, findNavController(R.id.main_host_fr))
}
}
nav_graph.xml
<?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/nav_graph"
app:startDestination="@id/action_history">
<fragment
android:id="@+id/action_history"
android:name="com.aredruss.qurio.ui.HistoryFragment"
android:label="HistoryFragment"
tools:layout="@layout/fragment_history" >
</fragment>
<fragment
android:id="@+id/action_settings"
android:name="com.aredruss.qurio.ui.SettingsFragment"
android:label="SettingsFragment"
tools:layout="@layout/fragment_settings" >
</fragment>
</navigation>
menu_main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_history"
android:icon="@drawable/ic_history"
android:title="History"
app:showAsAction="withText" />
<item
android:id="@+id/action_settings"
android:title="Settings"
android:icon="@drawable/ic_settings"
app:showAsAction="withText"
/>
</menu>
I've checked out this solution and it wasn't of any help.
You've overridden the wrong onPostCreate()
:
override fun onPostCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
You don't want the one that takes a PersistableBundle
- you want the regular one:
override fun onPostCreate(savedInstanceState: Bundle?) {
Now your onPostCreate()
will actually be called.
Of course, you shouldn't be using onPostCreate()
at all. Instead, you should be following the documentation, which specifically state that you should be using the following code to get your NavController
in onCreate()
:
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.main_host_fr) as NavHostFragment
val navController = navHostFragment.navController
which lets you write your entire code as:
// ViewBindingDelegate
private val binding: ActivityMainBinding by viewBinding(ActivityMainBinding::inflate)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Note how we actually set the content view from the binding
setContentView(binding.root)
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.main_host_fr) as NavHostFragment
val navController = navHostFragment.navController
binding.navigationBb.setupWithNavController(navController)
binding.mainNavBnv.setupWithNavController(navController)
}