Edit:
The original question was Is there a way to get a reference to a fragment that is displayed using a BottomNavigationView?. But I've figured some things out and realized I was asking the wrong question.
I'd like to get a reference to a fragment that is being displayed using a BottomNavigationView.
This is how my BottomNavigationView is being setup. It's in onCreate of an Activity.
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.bottom_nav_view_nav_host)
val appBarConfiguration = AppBarConfiguration(setOf(
R.id.navigation_first_list,
R.id.navigation_second
))
setupActionBarWithNavController(navController, appBarConfiguration)
bottomNav.setupWithNavController(navController)
I've tried to get the fragment with bottomNav.findFragment<TheFragmentType>() it throws an exception.
I was asking the wrong question originally. I can just use the navController to call the correct navigation component in order to show the right fragment with a back stack.
val bundle = bundleOf("someId" to "theId")
navController.navigate(R.id.action_navigation_list_to_details, bundle)
hello i am changing the label of fragment in navigation file but the fragment title in the tool bar isn't changing, any solution?
<fragment
android:id="#+id/navHostFragment"
android:name="com.example.androidtask.view.UsersList"
android:label="#string/user_list"
tools:layout="#layout/fragment_users_list">
<action
android:id="#+id/submit_user"
app:destination="#id/addUser" />
</fragment>
you need to setup ActionBar to work with navigation , add this code in activity onCreate()
val navController = findNavController(R.id.nav_host_fragment)// where nav_host_fragment is the id for your Main NavHost fragment
val appBarConfiguration = AppBarConfiguration(navController.graph)
setupActionBarWithNavController(navController, appBarConfiguration)
for more info check this
I've used the default bottom navigation template in Android Studio. This has been working fine so far. However, I'd like to change one of the buttons to a button which triggers a camera intent. I'm not sure how to do this since I assume the default implementation uses built-in functions which I can't change.
I believe setupWithNavController is responsible for navigating to the matching fragment. Is there any way to change this, so that I add a button that launches the camera?
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
val navController = findNavController(R.id.nav_host_fragment)
navView.setupWithNavController(navController)
}
}
Remove setupWithNavController and create your own menu.xml file. Then you can use your menu with adding like this:
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavigationBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="#menu/main" />
After that, you can implement setOnNavigationItemSelectedListener on your NavigationBar inside Kotlin class.
I am using Android architecture components with the Single Activity pattern on Android. The navigation pattern I am using is BottomNavigationView.I actually want the parent activity to have no ActionBar but setting my theme to be of type NoActionBar crashes the App. Setting Navigation in Activity has been done as below
val navController = findNavController(R.id.nav_host_fragment)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
val appBarConfiguration = AppBarConfiguration(
setOf(R.id.navigation_popular, R.id.navigation_top_rated, R.id.navigation_favorites)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
How do I set up bottom navigation to have no actionBar since I wish to have on of the some fragments with ActionBar, like CollapsingToolbarLayout?
I think you found the solution but for the others,
if you want to use the BottomNavigationView with the .NoActionBar theme so you should remove these lines:
val appBarConfiguration = AppBarConfiguration(
setOf(R.id.navigation_popular, R.id.navigation_top_rated, R.id.navigation_favorites)
)
setupActionBarWithNavController(navController, appBarConfiguration)
Using Android Navigation BottomNavigationView. Do not restrict activity with NoActionBar in manifest. Instead from your activity retrieve an instance of your support action bar and use its public method hide()
if (savedInstanceState == null) {
setupBottomNavigationBar()
} // Else, need to wait for onRestoreInstanceState
supportActionBar?.hide()
I'm currently using the Android Architecture Component's Navigation, but I'm running into an issue with my Navigation Drawer. It shows the hamburger menu when at my starting destination, but other Fragments are showing the up arrow. I believe I've setup my navigation_graph incorrectly.
Here you can see my nav drawer, showing 2 items, Home and Settings. When in either of these Fragments, you should see the Hamburger icon.
However, when navigating to the Settings Fragment, it shows the Up arrow.
navigation.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"
app:startDestination="#id/nav_home">
<!-- Start at HomeFragment -->
<fragment
android:id="#+id/nav_home"
android:name=".HomeFragment"
android:label="#string/home">
<!-- Navigate to the Search -->
<action
android:id="#+id/action_nav_home_to_nav_search"
app:destination="#id/nav_search" />
</fragment>
<fragment
android:id="#+id/nav_settings"
android:name=".SettingsFragment"
android:label="#string/settings">
<!-- Navigate to the Search -->
<action
android:id="#+id/action_nav_settings_to_nav_search"
app:destination="#id/nav_search" />
</fragment>
<fragment
android:id="#+id/nav_search"
android:name=".SearchFragment"
android:label="#string/search" />
</navigation>
I feel like HomeFragment and SettingsFragment should be related somehow but I'm not sure how to define that.
main_drawer.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item
android:id="#id/nav_home"
android:icon="#drawable/ic_home_white_24dp"
android:title="#string/home" />
<item
android:id="#id/nav_settings"
android:icon="#drawable/ic_settings_white_24dp"
android:title="#string/settings" />
</group>
</menu>
MainActivity
And then within MainActivity, I just set it up like this. I called setupActionBarWithNavController, but I also have to actually setup the nav drawer myself, and handle the onNavigationItemSelected.
private fun setupNavigation() {
navController = findNavController(R.id.mainNavigationFragment)
setupActionBarWithNavController(this, navController, drawer_layout)
val toggle = ActionBarDrawerToggle(
this, drawer_layout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close)
drawer_layout.addDrawerListener(toggle)
toggle.syncState()
nav_view.setNavigationItemSelectedListener(this)
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
val current = navController.currentDestination.id
if (item.itemId != current) {
navController.navigate(item.itemId)
}
drawer_layout.closeDrawers()
return true
}
build.gradle
// Navigation
implementation 'android.arch.navigation:navigation-fragment-ktx:1.0.0-alpha04'
implementation 'android.arch.navigation:navigation-ui-ktx:1.0.0-alpha04'
Thanks.
In newer alphas (I have 1.0.0-alpha07) they added possibility to define topLevelDestinationIds when calling AppBarConfiguration constructor.
So I setup my NavController like this
val navController = findNavController(R.id.nav_host_fragment)
val config = AppBarConfiguration(
setOf(
R.id.fistTopFragment,
R.id.secondTopFragment,
...
),
dr.drawerLayout
)
tb.setupWithNavController(navController, config)
Where dr is MaterialDrawer and tb of course Toolbar.
Then it behaves more like Gmail, at least for the ActionBarDrawerToggle, the back stack is still preserved. Since I must handle item selection in MaterialDrawer by myself, I'm going to reduce back stack actions using global navigation actions with inclusive popTo to the root fragment of the navigation graph and use something like a "Welcome screen" for now.
Another way around could be custom handling of the onBackPressed.
You must remove app:defaultNavHost="true" from your host fragment in activity layout first. Something like this
override fun onBackPressed() {
val navController = findNavController(R.id.nav_host_fragment)
if (navController.currentDestination == null
|| navController.currentDestination!!.id in setOf(
R.id.fistTopFragment,
R.id.secondTopFragment,
...
)
) {
super.onBackPressed()
} else {
navController.navigateUp()
}
}
Sorry about the code, I'm still learning Kotlin, so there is probably much nicer way of doing this.
I am afraid it is a feature of navigation component.
The action bar will also display the Up button when you are on a non-root destination and the drawer icon when on the root destination, automatically animating between them.
If you want, you can try to use setupWithNavController(NavigationView, NavController) and handle the toolbar yourself.
I made simple example for this issue. Solution is almost same as Almighty's answer. https://github.com/isaul32/android-sunflower
Create set of top level destinations at first
val topLevelDestinations = setOf(R.id.garden_fragment,
R.id.plant_list_fragment)
appBarConfiguration = AppBarConfiguration.Builder(topLevelDestinations)
.setDrawerLayout(drawerLayout)
.build()
and then override onSupportNavigateUp function like this
override fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(navController, appBarConfiguration)
}
Back arrow appearing on tab fragments associated with BottomNavigationView is an intended behaviour. However haven't seen it being used "as is" even in famous apps (Instagram, Youtube which have bottom tabs). If you navigate to different bottom tabs in Youtube app for example and click device back option, you'll notice it goes to previous tab fragment and not exit app. So bottomnavigationview tab fragments are not root destinations here.
Want to bring to notice additional important issues which you might encounter as you move forward:
It does not allow for reuse of fragments in combination with BottomNavigationView
https://issuetracker.google.com/issues/110373186
If you have multiple activities the up button does not navigate up to the previous activity
https://issuetracker.google.com/issues/79993862
However there are several hooks you can use to customise the behaviour:
onBackPressed - to close drawer layout if open
override fun onBackPressed() {
if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
drawerLayout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
Add navController.addOnNavigatedListener(..) and inside the listener customise HomeAsUpIndicator icon
Override onOptionsItemSelected to customise menu with id android.R.id.home action
Set custom fragment navigator to customise how your fragments are treated (replace or show/ hide)
navHostFragment = supportFragmentManager
.findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment? ?: return
val customNavigator = CustomFragmentNavigator(navHostFragment.requireContext(),
navHostFragment.childFragmentManager, navHostFragment.id)
navHostFragment.navController.navigatorProvider.addNavigator(customNavigator)
val inflater = navHostFragment.navController.navInflater
val graph = inflater.inflate(R.navigation.main_nav_graph)
navHostFragment.navController.graph = graph
Remember navigation arch component is still in alpha, so use it wisely.
If you want, you can use NavController.OnNavigatedListener and use below code to set title.
#Override
public void onNavigated(#NonNull NavController controller, #NonNull NavDestination destination) {
mActivityBinding.toolbar.setTitle(destination.getLabel());
}
Remember to set label in your navigation graph.
I think your menu items should be like this:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#id/nav_home"
android:icon="#drawable/ic_home_white_24dp"
android:title="#string/home" />
<item
android:id="#id/nav_settings"
android:icon="#drawable/ic_settings_white_24dp"
android:title="#string/settings" />
</menu>
Hope this helps
Happy coding...
I completely agree with the sentiment here but it is a part of the library
setupActionBarWithNavController
Sets up the ActionBar returned by AppCompatActivity.getSupportActionBar() for use with a NavController.
By calling this method, the title in the action bar will automatically be updated when the destination changes (assuming there is a valid label).
The start destination of your navigation graph is considered the only top level destination. On the start destination of your navigation graph, the ActionBar will show the drawer icon if the given DrawerLayout is non null. On all other destinations, the ActionBar will show the Up button. Call navigateUp(NavController, DrawerLayout) to handle the Up button.
this to me is broken and not the desired result, so what i'm doing is still using it with the navigation view using
navController = Navigation.findNavController(this, R.id.nav_host);
NavigationUI.setupWithNavController(navigationView,navController);
and then having a listener to update the title and anything else i want changed like this
navController.addOnNavigatedListener((controller, destination) -> {
setToolbarColour(R.color.primary);
switch (destination.getId()){
case R.id.dashBoard :
setToolbarColour(android.R.color.transparent);
break;
case R.id.requests :
toolbar.setTitle(getString(R.string.request));
break;
}
});
I'm then creating a new navigation graph for each of these fragments, not great but i get my intended result