Dynamic ActionBar title from a Fragment using AndroidX Navigation
AndroidAndroid JetpackAndroid Architecture-NavigationAndroid Problem Overview
I am using the new Navigation component from Android Jetpack.
The root Activity setup is quite simple:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
val navController = findNavController(R.id.navigationFragment)
setupActionBarWithNavController(navController)
bottomNavigationView.setupWithNavController(navController)
}
It works well when the Fragment's title is defined in the navigation graph. But for one Fragment, I want to set the title dynamically.
I tried with findNavController().currentDestination.label = "Hello world"
but it does nothing.
I could of course use a trick like (activity as? AppCompatActivity)?.supportActionBar?.title = "Hello world"
, but I feel that it will break the magic that setupActionBarWithNavController()
does for me. It there any way to update the Action Bar title dynamically?
Android Solutions
Solution 1 - Android
As of 1.0.0-alpha08
, you can have the NavigationUI bits dynamically set the title... if the dynamic bits are arguments on the navigation action.
So, for example, in your navigation graph, you could have something like this:
<fragment
android:id="@+id/displayFragment"
android:name="com.commonsware.jetpack.sampler.nav.DisplayFragment"
android:label="Title: {title}" >
<argument
android:name="modelId"
app:argType="string" />
<argument
android:name="title"
app:argType="string" />
</fragment>
Here, the android:label
attribute for our <fragment>
has an argument name wrapped in braces ({title}
in "Title: {title}"
. The app bar's title will then be set to the value of the label, with {title}
replaced by the value of the title
argument.
If you need something more elaborate than that — for example, you want to look up the model by ID and read a property from it — you will need to use more manual approaches, such as those outlined in other answers to this question.
Solution 2 - Android
The title can be changed in the fragment by casting the activity as AppCompatActivity
.
Kotlin
(requireActivity() as AppCompatActivity).supportActionBar?.title = "Hello"
Java
((AppCompatActivity) requireActivity()).getSupportActionBar().setTitle("Hello");
Solution 3 - Android
Taking consideration that your host activity is MainActivity
, just add the following code to your MainActivity
's onCreate
fun
val navController = Navigation.findNavController(this, R.id.nav_host_fragment)
// setting title according to fragment
navController.addOnDestinationChangedListener {
controller, destination, arguments ->
toolbar.title = navController.currentDestination?.label
}
Solution 4 - Android
If you use toolbar on setSupportActionBar in Activity and you would like to change its title in fragment, then below code may gonna help you ;)
(requireActivity() as MainActivity).toolbar.title = "Title here"
Solution 5 - Android
As of now, The Jetpack Navigation Architecture components do not provide any "built in" way to do this, and you'll have to implement your own "custom" method for doing it.
There is an existing feature request to get functionality for dynamic labels on destinations added to the new Jetpack Navigation Architecture Components. If you are here because you want/need this functionality, please star the existing feature request, here: https://issuetracker.google.com/issues/80267266
Solution 6 - Android
Remove label from graph.xml file
android:label="fragment_info"
and use old school approach if you want to set title of the fragment dynamically from the fragment itself
getActivity().setTitle("Your Title");
Solution 7 - Android
Well, now the Navigation UI supports this feature. Now the ActionBar
title changes dynamically. You just have to setup the ActionBar
with the NavController
.
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
preferedTheme()
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
navController = findNavController(R.id.nav_controller_fragment)
appBarConfiguration = AppBarConfiguration(navController.graph)
setupActionBarWithNavController(navController, appBarConfiguration)
}
And set action bar label in nav graph:
<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/mobile_navigation"
app:startDestination="@id/mainFragment">
<fragment android:id="@+id/mainFragment"
android:name="com.cinderellaman.general.ui.fragments.MainFragment"
android:label="General"
tools:layout="@layout/main_fragment"/>
And now its also support Navigate Up:
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
Solution 8 - Android
Another solution is to use ViewModel and LiveData, attach viewmodel to your activity and fragments, add a livedata field inside viewmodel
val title = MutableLiveData<String>()
From your activity observe this field, and if it is changed update the toolbar title
viewModel?.title?.observe(this, Observer {
my_toolbar.title=it
})
From your desired fragment change the title field inside the viewmodel
viewModel?.title?.value="New title"
Solution 9 - Android
Until the issue will be fixed, simple listener is working to me:
/**
* Temporary solution to dynamically change title of actionbar controlled by Navigation component
* Should be removed as soon as the bug on Navigation will be fixed: (https://issuetracker.google.com/issues/80267266)
*/
interface TempToolbarTitleListener {
fun updateTitle(title: String)
}
class MainActivity : AppCompatActivity(), TempToolbarTitleListener {
...
override fun updateTitle(title: String) {
binding.toolbar.title = title
}
}
change title from fragment:
(activity as TempToolbarTitleListener).updateTitle("custom title")
Solution 10 - Android
You can add addOnNavigatedListener inside your activity, and based on current destination change the title
findNavController(nav_host_fragment).addOnNavigatedListener { controller, destination ->
when(destination.id) {
R.id.destination1 -> {
my_toolbar.title= "Some title"
}
R.id.destination2 -> {
my_toolbar.title= "Othertitle"
}
}
}
Solution 11 - Android
If your title is received from a concatenated string, for example:
ActionBar actionBar = ((AppCompatActivity)
requireActivity()).getSupportActionBar();
if(actionBar!=null)
actionBar.setTitle(count + "items");
Solution 12 - Android
As per the below solutions, there is no need to access the toolbar manually.
If you are going to pass the Model into the destinations fragment. Then you can do as below.
Override toString()
method in your model class
data class UserModel(
val userId: String = ""
val firstName: String = "",
val lastName: String = ""
) {
override fun toString(): String {
return "$firstName $lastName"
}
}
Now, in the nav_grap.xml
file, do as below. android:label="{UserData}"
will be fetch label from toString() method of model class.
<fragment
android:id="@+id/messagesFragment"
android:name="com.app.mydemoapp.ui.messages.MessagesFragment"
android:label="{UserData}"
tools:layout="@layout/fragment_messages">
<argument
android:name="UserData"
app:argType="com.app.mydemoapp.model.UserModel" />
</fragment>
I hope, this will be helpful.
Solution 13 - Android
Based on @kaustubh-trivedi answer, and if you are using MVVM (like the Android Studio FirstFragment/SecondFragment example):
KOTLIN version
val navController = Navigation.findNavController(this, R.id.nav_host_fragment)
// setting title according to fragment
navController.addOnDestinationChangedListener {
controller, destination, arguments ->
toolbar.title = navController.currentDestination?.label
}
JAVA version
navController.addOnDestinationChangedListener(new NavController.OnDestinationChangedListener() {
@Override
public void onDestinationChanged(@NonNull NavController controller, @NonNull NavDestination destination, @Nullable Bundle arguments) {
binding.toolbar.setTitle(destination.getLabel());
}
});
Solution 14 - Android
On trying the activity's title it seems to override the title for fragment. Being on safe side you must put on onResume
.
override fun onResume() {
super.onResume()
activity?.toolbar.title = "YOUR_TITLE_HERE"
}
it works for me !
Note : Must have Toolbar Widget in activity
Add toolbar like this in your activity's xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.appbar.AppBarLayout>
<!-- Other Widgets -->
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Solution 15 - Android
you can remove android:label in navigation graph then write in onCreateView()
activity?.title="your title"