I need to navigate a stack of fragments and I am navigating back using the toolbar back button. Can I override the back button pressed to set a custom animation, for example slide out?
Here is the code for the toolbar.
private fun setupToolbar() {
val appBarConfiguration = AppBarConfiguration(navController.graph, drawer_layout)
val toolbar = toolbar as Toolbar
setSupportActionBar(toolbar)
toolbar.setupWithNavController(navController, appBarConfiguration)
val ab: ActionBar? = supportActionBar
ab?.setDisplayShowTitleEnabled(false) // disable the default title element here (for centered title)
setupSearchQueryListener()
}
In your setup code, one more thing is needed:
toolbar.setNavigationOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
In your fragments, you could do this:
protected lateinit var backPressedCallback: OnBackPressedCallback
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
backPressedCallback = requireActivity().onBackPressedDispatcher.addCallback(this) {
// your code
}
}
The above will enable you to intercept back navigation uniformly and execute your code in question (you can even block/unblock it by playing with backPressedCallback.isEnabled flag). The above was tested. Speaking of setting navigation animation, I was only playing with xml defined action based animations:
<action
android:id="#+id/toYourDest"
app:destination="#+id/yourDest"
app:enterAnim="#anim/your_slide_in_right"
app:exitAnim="#anim/your_slide_out_left"
app:popEnterAnim="#anim/your_slide_in_left"
app:popExitAnim="#anim/your_slide_out_right" />
Related
I'm implementing this bottom navigation pattern but with a splash fragment.
My issue is when I navigate throw different fragments with bottom menu and I press to go back, I don't go back to the home fragment, instead of this, I return to the previous fragment.
For example, I have fragments A-B-C:
Now I'm on fragment A and I press to go to B.
Then I press to go to C (from B).
Then I press to go back.
The result is I'm getting back to B, not to fragment A (what I really want!).
(If I set in the navigation graph app:startDestination -> fragment A - instead of login fragment - everything goes well).
Here is my 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/main_navigation"
app:startDestination="#id/splashFragment">
<fragment
android:id="#+id/splashFragment"
android:name="application.SplashFragment"
tools:layout="#layout/fragment_splash">
<action
android:id="#+id/action_splashFragment_to_fragmentA"
app:destination="#id/fragmentA"
app:popUpTo="#id/main_navigation"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="#+id/fragmentA"
android:name="application.fragmentA"
android:label="#string/fragmentA"
tools:layout="#layout/fragmentA" />
<fragment
android:id="#+id/fragmentB"
android:name="application.fragmentB"
android:label="fragmentB"
tools:layout="#layout/fragmentB" />
<fragment
android:id="#+id/fragmentC"
android:name="application.fragmentC"
android:label="#string/fragmentC"
tools:layout="#layout/fragmentC" />
And here my MainActivity:
class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Toolbar & Navigation
setSupportActionBar(toolbar)
navController = findNavController(R.id.nav_host)
// AppBarConfiguration with the correct top-level destinations
appBarConfiguration = AppBarConfiguration(
topLevelDestinationIds = setOf(
R.id.fragmentA,
R.id.fragmentB,
R.id.fragmentC
)
)
// This allows NavigationUI to decide what label to show in the action bar
// By using appBarConfig, it will also determine whether to
// show the up arrow or drawer menu icon
setupActionBarWithNavController(navController, appBarConfiguration)
// Set destinations to left and bottom navigation
bottom_navigation.setupWithNavController(navController)
// Set visibility for NavigationView & Toolbar
visibilityNavElements()
}
// Allows NavigationUI to support proper up navigation or the drawer layout
// drawer menu, depending on the situation
override fun onSupportNavigateUp() = navController.navigateUp(appBarConfiguration)
private fun visibilityNavElements() {
findNavController(R.id.nav_host).addOnDestinationChangedListener { _, destination, _ ->
when (destination.id) {
R.id.splashFragment -> {
toolbar.visibility = View.GONE
bottom_navigation.visibility = View.GONE
}
else -> {
toolbar.visibility = View.VISIBLE
bottom_navigation.visibility = View.VISIBLE
}
}
}
}
}
Thanks!
You can handle OnbackPress in any fragment like below and is the recommended way . You can use it in Fragment C and when back is pressed you can navigate to Fragment A
in Java
OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) {
#Override
public void handleOnBackPressed() {
}
};
requireActivity().getOnBackPressedDispatcher().addCallback(this, onBackPressedCallback);
in Kotlin
val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
// Handle the back button event
}
I've setup my bottom navigation with navHostController, I've a container activity which has a toolbar, I want to change the icon in toolbar based on which fragment I'm on, this was easy if I didn't use navigation Component library.
MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bottomNavigationView.setupWithNavController(dashboardNavHostFragment.findNavController())
}
I want to change the icon in the toolbar to the fragment active in the bottom navigation.
You can use the addOnDestinationChangedListener.
Something like:
navController.addOnDestinationChangedListener { _, destination, _ ->
if(destination.id == R.id.xxxx) {
toolbar.setNavigationIcon(R.drawable.xxxx)
} else {
//
}
}
I'm using jetpack navigation component to build a navigation drawer, I want to change hamburger icon of the toolbar, I tried many solutions like bellow but they don't work
app:navigationIcon="#drawable/menu"
also
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeAsUpIndicator(R.drawable.menu);
and this is my code
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
navController=findNavController(R.id.nav_host_fragment)
appBarConfiguration=AppBarConfiguration(setOf(R.id.nav_acceuil,R.id.nav_notifications,R.id.nav_gerer,R.id.nav_deconnexion),drawer_layout)
setupActionBarWithNavController(navController,appBarConfiguration)
nav_view.setupWithNavController(navController);
}
You can use the addOnDestinationChangedListener:
navController.addOnDestinationChangedListener { controller, destination, arguments ->
if (destination.id == R.id.nav_xxxx){
supportActionBar?.setHomeAsUpIndicator(R.drawable.xxxx)
}
}
I am using a navigation graph in my app. I have a bottom navigation bar. I am staring my navigation components with the following in my Main Activity:
setContentView(R.layout.activity_main)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
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_artist_list, R.id.navigation_dashboard, R.id.navigation_notifications))
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
This starts my navigation components, which inflate the fragement setup in R.id.navigation_artist_list.
From an observer in that fragment I am navigating to another fragment with:
viewModel.selectedAlbum.observe(this, Observer { artist ->
val action = HomeFragmentDirections.actionNavigationHomeToAlbumFragment(artist)
root.findNavController().navigate(action)
})
However, none of the mechanisms available for navigation work properly. The back button just refreshes the fragment and populates the list in the fragment again. The back button on the action bar is completely ignored.
Do I need to do something else to have the proper back button behavior, to go back to the previous fragment?
In yout MainActivity, override the onSupportNavigateUp() method to call navigateUp() in the navigation controller
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.nav_host_fragment)
return navController.navigateUp()
}
I need a second activity with a nav graph and have a return button in toolbar to the first activity that also contains a nav graph
In my second activity I have onSupportNavigateUp and setupActionBarWithNavController when entering the fragments if the arrow back button appears but in the activity no.
Try adding setHomeButtonEnabled and setDisplayHomeAsUpEnabled in both the activity and the fragment and if the button appears back, but when I enter some fragment in front and return to the fragment startDestination disappears the button back
I just need to keep the button back in the activity and solve my problem
You can do it by specifying a setFallbackOnNavigateUpListener:
private fun setupToolbar() {
val navController = findNavController(R.id.nav_host_fragment)
val appBarConfiguration =
AppBarConfiguration.Builder()
.setFallbackOnNavigateUpListener { onNavigateUp() }
.build()
dataBinding.toolbar.apply {
setupWithNavController(navController, appBarConfiguration)
}
}
And then do whatever you want in the Activity's:
override fun onNavigateUp(): Boolean {
finish()
return true
}
You can't, activity has their own toolbars and in your case they have two different NavControllers. So your second activity manage NavUp Button for his fragment and when start Destination fragment comes NavUpButton(Backbutton) disappear because it has no destination left behind. And if you programmatically show NavUp Button on start destination of that (2nd activity) and manage onClick and start first activity that always goes to Start destination of first activity's fragment because it has it's own Nav Controller.
Problem is that Navigation UI not works like that. The better approach is use only one activity with multiple fragments. And use any other approach to solve your problem within the same nav controller.
Add setHomeButtonEnabled func. to your returning action. If you are returning with button add it to onClick or with backPress, override backPress.
With this solve : You will set your button enable, when you try to return your startDestination.
I created an interface to show/hide up button from the nav host activity. Here is how the activity implements the interface methods to show/hide up button:
override fun showUpButton() {
val navController = this.findNavController(R.id.nav_host)
val listener = AppBarConfiguration.OnNavigateUpListener { navController.navigateUp() }
val abc = AppBarConfiguration.Builder().setFallbackOnNavigateUpListener(listener).build()
NavigationUI.setupActionBarWithNavController(this, navController, abc)
}
override fun hideUpButton() {
val navController = this.findNavController(R.id.nav_host)
NavigationUI.setupActionBarWithNavController(this, navController)
}
Here the method in the activity when up button pressed:
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.nav_host)
if(!navController.navigateUp()){ // When in start destination
onBackPressed()
}
return navController.navigateUp()
}
In a fragment can listen whenever back button (NOT up button) pressed:
private fun setupBackPress() {
requireActivity()
.onBackPressedDispatcher
.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
}
})
}