Animation for BottomNavigation Fragments with Architecture Navigation Components - android

I have successfully integrated the bottom navigation with the latest android architecture navigation components. The following is my complete code.
Navigation
<?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/mobile_navigation"
app:startDestination="#+id/navigation_home">
<fragment
android:id="#+id/navigation_home"
android:name="in.zedone.bottomsample.ui.home.HomeFragment"
android:label="#string/title_home"
tools:layout="#layout/fragment_home" />
<fragment
android:id="#+id/navigation_saloons"
android:name="in.zedone.bottomsample.ui.saloons.SaloonsFragment"
android:label="#string/title_saloon"
tools:layout="#layout/fragment_saloons" />
<fragment
android:id="#+id/navigation_offers"
android:name="in.zedone.bottomsample.ui.offers.OffersFragment"
android:label="#string/title_offer"
tools:layout="#layout/fragment_offers" />
<fragment
android:id="#+id/navigation_account"
android:name="in.zedone.bottomsample.ui.account.AccountFragment"
android:label="#string/title_account"
tools:layout="#layout/fragment_account" />
</navigation>
BottomNavigationView
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:labelVisibilityMode="labeled"
app:itemTextAppearanceActive="#style/BottomNavigationView.Active"
app:itemTextAppearanceInactive="#style/BottomNavigationView"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/bottom_nav_menu" />
MainActivity
BottomNavigationView navView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder(
R.id.navigation_home, R.id.navigation_saloons, R.id.navigation_offers,R.id.navigation_account)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView, navController);
Now how can I add transition/animation on select each tab/fragment in bottom navigation?

Instead of using setupWithNavController function, follow this way.
First, create your NavOptions which include animation shown below.
val options = NavOptions.Builder()
.setLaunchSingleTop(true)
.setEnterAnim(R.anim.enter_from_bottom)
.setExitAnim(R.anim.exit_to_top)
.setPopEnterAnim(R.anim.enter_from_top)
.setPopExitAnim(R.anim.exit_to_bottom)
.setPopUpTo(navController.graph.startDestination, false)
.build()
Then use setOnNavigationItemSelectedListener to navigate with animation like that.
bottomNavigationView.setOnNavigationItemSelectedListener { item ->
when(item.itemId) {
R.id.fragmentFirst -> {
navController.navigate(R.id.fragmentFirst,null,options)
}
R.id.fragmentSecond -> {
navController.navigate(R.id.fragmentSecond,null,options)
}
R.id.fragmentThird -> {
navController.navigate(R.id.fragmentThird,null,options)
}
}
true
}
Finally, you should prevent same item selection case so you can add below code.
bottomNavigationView.setOnNavigationItemReselectedListener { item ->
return#setOnNavigationItemReselectedListener
I used bottomNavigation like that in my project to add animation for page transitions.
I hope it helped.

its work with BottomNavigationView's siblings fragment also(JetPack Navigation Components)
// FragmentA.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
exitTransition = MaterialFadeThrough()
}
// FragmentB.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enterTransition = MaterialFadeThrough()
}

You can also do it with keeping setupWithNavController by overriding onCreateAnimation() in each Fragment and checking whether or not you're entering or exiting the Fragment with the enter parameter and then creating the appropriate Animation with AnimationUtils.loadAnimation(context, animationId).
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
return if (enter) {
AnimationUtils.loadAnimation(context, R.anim.fade_in)
} else {
AnimationUtils.loadAnimation(context, R.anim.fade_out)
}
}
EDIT as a response to iloo:
According to Material Design (https://material.io/components/bottom-navigation#behavior) you shouldn't be doing that in the first place, but I guess it's still achievable.
Since when using setupWithNavController all destinations are top level there will
ever be only one previousBackStackEntry whose destination points to the home
destination, therefore previousBackStackEntry is not much of a help in figuring out which Fragment are you coming from.
Other approach could be to have a public variable in the Activity, where you will store the
last destination you were in and set that variable in each Fragment upon resuming.
And you can check that variable in onAnimationCreate to know which Fragment you're coming from
and apply an appropriate animation.

I'm kinda new to the Android Navigation but this is what I think you can try if you like to change the default animation
put those file in your anim directory
nav_default_enter_anim.xml
nav_default_exit_anim.xml
nav_default_pop_enter_anim.xml
nav_default_pop_exit_anim.xml
and the default animation for the transactions will change to the animation you put in the above files.

Related

How to change fragment and selected item in bottom navigation menu in android studio

I wanted my app to change fragment view and the selected item in bottom navigation menu. I tried doing this:
private void replaceFragment(Fragment fragment){
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frameLayout,fragment);
fragmentTransaction.commit();
}
It changes the fragment but the item selected in the bottom navigation menu doesn't.
I tried doing startActivity(new Intent(activity.this, destination.class)); but it doesn't suit the app because it also refreshes the bottom nav menu which is not what I wanted. What I wanted is the fragment and the selected item in bottom navigation menu to both change.
this is the fragment where the button is
I want the app to go here when the book now button is clicked:
this is where i wanted it to go
As you can see in this picture, the fragment changed but the item selected does not. This is when I did the replaceFragment code when i use the replaceFragment code
You can use Bottom Navigation view implementation from Jet pack Navigation component for this work. it is easy and clear. Bottom navigation will handle all item selected use cases by default. Also you can customized it easily.
Here I have added Kotlin example for bottom navigation setup.
Step 01:
add the following dependencies to your app's build.gradle file:
dependencies {
val nav_version = "2.4.2"
// Java language implementation
implementation("androidx.navigation:navigation-fragment:$nav_version")
implementation("androidx.navigation:navigation-ui:$nav_version")
// Kotlin
implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
implementation("androidx.navigation:navigation-ui-ktx:$nav_version")
}
Step 02: Add a NavHostFragment via XML as part of an app's main activity:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/bottom_navigation_menu" />
<fragment
android:id="#+id/nav_host_fragment_activity_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="#id/nav_view"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/main_nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
Step 03: init Bottom navigation view and navigation controller from your main main activity OnCreate()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(LayoutInflater.from(this))
setContentView(binding.root)
val navView : BottomNavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_activity_main)
navView.setupWithNavController(navController)
// Get bottom nav listener
navController.addOnDestinationChangedListener
{ _, destination, _ ->
when (destination.id) {
R.id.A_Fragment -> {
// Add your requirement here base with destination
}
else -> {
// Add your requirement here
}
}
// Manage bottom nav visibility and etc
if (destination.id in arrayOf(
R.id.A_Fragment,
R.id.B_Fragment,
R.id.C_Fragment
)) {
navView.visibility = View.VISIBLE
this.supportActionBar?.show()
setupToolbar()
}
else {
navView.visibility = View.GONE
this.supportActionBar?.hide()
// No Toolbar
}
}
}
Also you can define destination with navigation graph with this implementation.
If you need to add or customized bottomNaivationView item badge, you can use following example fun:
private fun setupBadge(bottomNavigationView: BottomNavigationView) {
val menu: Menu = bottomNavigationView.menu
val your_A_MenuItem: MenuItem = menu.getItem(1)
val your_B_MenuItem: MenuItem = menu.getItem(2)
val your_A_Badge = bottomNavigationView.getOrCreateBadge(your_A_MenuItem.itemId)
val your_B_Badge = bottomNavigationView.getOrCreateBadge(your_B_MenuItem.itemId)
// Add numbers
your_A_Badge.number = 10
your_A_Badge.isVisible = true
}

DrawerLayout with Navigation component: Hamburger menu on non-home top level destinations doesn't load the drawer

I'm setting up a DrawerLayout in my one-activity Navigation component setup, pretty much as outlined on the guide here.
Here is my main activity's code:
class MainActivity : AppCompatActivity(), SubjectFragment.OnListFragmentInteractionListener {
lateinit var drawerLayout: DrawerLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
drawerLayout = findViewById<DrawerLayout>(R.id.main_drawer_layout)
val navView = findViewById<NavigationView>(R.id.main_navigation_view)
val sroToolbar = findViewById<Toolbar>(R.id.main_toolbar)
val navController = findNavController(R.id.nav_host_fragment)
navView.setupWithNavController(navController)
setSupportActionBar(sroToolbar)
val appBarConfiguration = AppBarConfiguration(
setOf(R.id.subjectFragment, R.id.aboutFragment, R.id.settingsFragment),
drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
}
override fun onSupportNavigateUp(): Boolean {
return findNavController(R.id.nav_host_fragment).navigateUp(drawerLayout)
}
Above, I'm using AppBarConfiguration to set up the three top level destinations (subjectFragment, aboutFragment, settingsFragment), with the subjectFragment being the home destination in the navigation graph. Here's my nav graph:
<?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/sro_nav_graph"
app:startDestination="#id/subjectFragment">
<fragment
android:id="#+id/subjectFragment"
android:name="black.old.spacedrepetitionowl.SubjectFragment"
android:label="Spaced Repetition Owl: Subjects"
tools:layout="#layout/fragment_subject_list" >
<action
android:id="#+id/action_subjectFragment_to_aboutFragment"
app:destination="#id/aboutFragment" />
<action
android:id="#+id/action_subjectFragment_to_settingsFragment"
app:destination="#id/settingsFragment" />
</fragment>
<fragment
android:id="#+id/aboutFragment"
android:name="black.old.spacedrepetitionowl.AboutFragment"
android:label="Spaced Repetition Owl: About"
tools:layout="#layout/fragment_about" />
<fragment
android:id="#+id/settingsFragment"
android:name="black.old.spacedrepetitionowl.settingsFragment"
android:label="fragment_settings"
tools:layout="#layout/fragment_settings" />
</navigation>
When I start the app, it shows the subjectFragment and the hamburger menu opens the drawer when tapped. This so far is the correct behavior. My drawer then shows three items: Subjects, Settings, and About.
When I select Settings/About from the menu, this then opens the Settings/About fragment correctly.
My problem is, when I'm on those two screens, tapping the hamburger icon will then load the SubjectFragment, instead of making the drawer show up. Here's a GIF of what happened:
The behavior I want is so that the drawer loads each time the hamburger menu is tapped on each of those destinations. What might be the issue here?
Aha! I figured this out by comparing it to a new project made using the 'Navigation Drawer Activity' as project template.
As it turns out, the appBarConfiguration needs to be used as the parameter for navigateUp so that the top-level destinations setup are obeyed:
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}

Androidx Bottom Navigation Bar keeps highlight on hideable nav item

I'm hiding a bottom navigation item when user is not authenticated (that item will show up once the user is authenticated, which in this case, is a Messages Fragment). That works well, but when that fragment is shown and selected, the previous navigation menu that I was in is still highlighted, even if I'm on that fragment. Also, once I chose another navigation item, Messages is still highlighted on the bottom navigation bar. Refer to the screenshots below:
When the Messages is not yet selected
When I select Message Fragment (I was from Home Fragment before that)
When I go back to any other Fragment aside from Messages (in this case, Home Fragment)
Here's my MainActivity (Notice that there's navView.menu.findItem(R.id.navigation_messages).isVisible, which is the main cause of this behavior in bottom navigation bar)
class MainActivity : AppCompatActivity() {
private var authState : Int = 1
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)
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home, R.id.navigation_explore, R.id.navigation_messages, R.id.navigation_account
)
)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
when(authState) {
0 -> navView.menu.findItem(R.id.navigation_messages).isVisible = false
1 -> navView.menu.findItem(R.id.navigation_messages).isVisible = true
}
}
override fun onSupportNavigateUp(): Boolean {
return navigateUp(Navigation.findNavController(this, R.id.nav_host_fragment), AppBarConfiguration(setOf(
R.id.navigation_home, R.id.navigation_explore, R.id.navigation_messages, R.id.navigation_account
)))
}
}
Also, here's my activity_main.xml file:
<androidx.constraintlayout.widget.ConstraintLayout 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">
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="0dp"
android:layout_marginEnd="0dp"
android:background="?android:attr/windowBackground"
app:itemHorizontalTranslationEnabled="false"
app:labelVisibilityMode="labeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:menu="#menu/bottom_nav_menu" />
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
android:layout_marginBottom="56dp"
app:layout_constraintBottom_toTopOf="#+id/nav_view"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:navGraph="#navigation/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
To note, I'm using Android Jetpack Navigation Architecture Components, which made other similar solutions like this, this, or this, not that viable on this case.

Navigation Drawer replace fragment with another problematically retaining the drawer option

I have a basic app, containing a drawer layout. Each option in the drawer opens up a new fragment page for the user to view. Here is the drawer layout code.
<androidx.drawerlayout.widget.DrawerLayout
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/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
layout="#layout/app_bar_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.navigation.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_main"
app:menu="#menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
Here is the app_bar_main.xml code.
<androidx.coordinatorlayout.widget.CoordinatorLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".DashBoardActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="#layout/content_main" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
And here is the content_main.xml. And also the main activity DashBoardActivity.kt
class DashBoardActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dashboard)
val toolbar: Toolbar = findViewById(R.id.toolbar)
setSupportActionBar(toolbar)
val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
val navView: NavigationView = 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.
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.nav_company_emissions, R.id.nav_your_emissions, R.id.nav_contact,
R.id.nav_privacy_policy
), drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.main, menu)
return true
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
}
From my understanding when you click a button in the drawer layout the fragment in the host fragment is replaced with the relevant clicked on fragment.
My questions is... How do I programatically put another fragment in this nav host view which hasn't been set up with the drawer layout. My problem is one of the fragments contains a profile for a user with an edit button. I want to open a new fragment when the edit button is clicked and wish for the user to still have access to the drawer when they do so don't want to go to another activity.
In my mind I just want to replace the profile_fragment with the edit_fragment. But when I've tried something like this below its placed the fragment onto of the profile one so that you have this weird looking screen with two fragments on top of one another.
val frag: Fragment = EditYourEmissionsFragment()
val fragMan:FragmentManager = activity!!.supportFragmentManager
val fragTran: FragmentTransaction = fragMan.beginTransaction()
fragTran.add(R.id.nav_host_fragment, frag)
fragTran.addToBackStack(null)
fragTran.commit()
I'm still a relative novice when it comes to Android and Kotlin so am struggling to understand how I would go about doing my user case.
Scenario for trying to help paint a picture of what I want to happen:
User loads app and lands on home_fragment.
Opens drawer menu, clicks profile_fragment.
Once in profile_fragment opens the user presses edit.
A new fragment takes over the screen, but retains the ability to open the menu drawer.
User can now either open the drawer menu and navigate to other places across the app. Or save their profile and get taken back to the profile_fragment.
Thank you in advance for any help. Apologies if its not particularly clear.
include the EditYourEmissionsFragment in the navigationGraph like this:
<fragment
android:id="#+id/EditYourEmissionsFragment"
android:name="com.example.application.EditYourEmissionsFragment"
android:label="Edit Profile"
tools:layout="#layout/fragment_edit_omissions"/>
and then on your ProfileFragment set the onClick to open the destination like this;
val navController = Navigation.findNavController(view)
navController.navigate(R.id.EditYourEmissionsFragment)
and in your MainActivity
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.my_nav_host)
return navController.navigateUp(myAppBarConfiguration) || super.onSupportNavigateUp()
}

Jetpack navigation: Title and back/up arrow in the action bar?

I have installed the latest canary version of Android Studio, and followed this (https://developer.android.com/topic/libraries/architecture/navigation/navigation-implementing) instruction to implement a simple two page navigation. Basically page1 has a button, and when it is clicked, the app shows page2.
It works, but there is one problem... It does not seem to do anything with the action bar automatically. Is it supposed to show up/back arrow and the "Label" attribute on the action bar automatically by the navigation library? Or am I supposed to do all the work manually as before? I want to show the back arrow and "Details" on action(tool) bar when page2 is showing.
On button click at page 1.
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
{
button1.setOnClickListener {
val nav = NavHostFragment.findNavController(this);
nav.navigate(R.id.show_page2)
}
}
Main activity XML. By default it was the default Action Bar, I have replaced it with a ToolBar. There was no difference.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_height="?attr/actionBarSize"
android:elevation="4dp"
android:background="?attr/colorPrimary"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:theme="#style/ThemeOverlay.AppCompat.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
android:layout_width="match_parent">
</androidx.appcompat.widget.Toolbar>
<fragment
android:id="#+id/my_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/toolbar"
app:navGraph="#navigation/nav_graph"/>
</androidx.constraintlayout.widget.ConstraintLayout>
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/page1">
<activity
android:id="#+id/mainActivity2"
android:name="com.android.navtest.MainActivity"
android:label="activity_main"
tools:layout="#layout/activity_main"/>
<fragment
android:id="#+id/page1"
android:name="com.android.navtest.BlankFragment2"
android:label="Home page"
tools:layout="#layout/page1">
<action
android:id="#+id/show_page2"
app:destination="#id/page2"
app:enterAnim="#anim/anim1"
app:popExitAnim="#anim/anim2"/>
</fragment>
<fragment
android:id="#+id/page2"
android:name="com.android.navtest.BlankFragment"
android:label="Details"
tools:layout="#layout/page2"/>
</navigation>
You can connect your ActionBar to a NavController using NavigationUI.setupActionBarWithNavController(). This is generally done in your Activity, right after you call setSupportActionBar():
supportActionBar = findViewById<Toolbar>(R.id.toolbar)
// Get the NavController for your NavHostFragment
val navController = findNavController(R.id.nav_host_fragment)
// Set up the ActionBar to stay in sync with the NavController
setupActionBarWithNavController(navController)
This approach is covered in the Navigation talk at Google I/O 2018.
If you want to have navigation back button hidden in more than one place (default is only for home fragment) you can add ids of fragments to AppBarConfiguration and pass this as second parameter of setupActionBarWithNavController, for example:
val appBarConfiguration = AppBarConfiguration(setOf(R.id.splashFragment, R.id.onboardingFragment, R.id.homeFragment))
setupActionBarWithNavController(findNavController(R.id.nav_host), appBarConfiguration)
This is what I have done.
onSupportNavigateUp is called when the user navigates up and it set again. by calling this setupActionBarWithNavController tell android to update the title of toolbar.
navigateUp Handles the Up button by delegating its behavior to the given NavController. This should generally be called from AppCompatActivity.onSupportNavigateUp().
private lateinit var appBarConfiguration: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityGameConfigBinding =
DataBindingUtil.setContentView(this, R.layout.activity_game_config)
supportActionBar?.show()
val navController = Navigation.findNavController(this, R.id.myNavHostFragment)
NavigationUI.setupActionBarWithNavController(this, navController, null)
appBarConfiguration = AppBarConfiguration.Builder(navController.graph)
.build()
NavigationUI.setupWithNavController(binding.navView, navController)
}
override fun onSupportNavigateUp(): Boolean {
val navController = Navigation.findNavController(this, R.id.myNavHostFragment)
return NavigationUI.navigateUp(navController, appBarConfiguration)
}
my solution with binding - the code is in MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.main_activity)
navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
setSupportActionBar(toolbar)//needs to be after binding
toolbar.setupWithNavController(navController,AppBarConfiguration(navController.graph))
}
as for the titles - first I removed labels (android:label) from the fragments in navigation graph (label overwrites title from what I've tested)
<fragment
android:id="#+id/productListFragment"
android:name="com.example.ProductListFragment"
android:label="TO_BE_REMOVED"
tools:layout="#layout/product_list_fragment">
<action
android:id="#+id/action_productListFragment_to_mainMenuFragment"
app:destination="#id/mainMenuFragment" />
</fragment>
each fragment sets the title and subtitle in onResume, here example from ProductListFragment
override fun onResume() {
super.onResume()
val actionBar = (activity as AppCompatActivity).supportActionBar
actionBar?.title = getString(R.string.product_list_title)
actionBar?.subtitle = getString(R.string.product_list_subtitle)
}

Categories

Resources