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

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)
}

Related

Android, back button navigation with FragmentContainerView close the app instead of going to the previous fragment [duplicate]

This question already has answers here:
android navigation component up button, arrow displays but popTo not working
(2 answers)
Closed 1 year ago.
I have an AppCompatActivity with a FragmentContainerView and a BottomNavigationView.
When I navigate from a fragment_A to the fragment_B(which are not in the bottom menu) a back button appears, the problem is that the back button closes the app instead of going back to the previous fragment_A.
My activity xml:
<?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:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:liftOnScroll="true">
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/homeToolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_scrollFlags="scroll|enterAlways|snap" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host_fragment_activity_home"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="#+id/nav_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/appBarLayout"
app:navGraph="#navigation/mobile_navigation" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom"
app:layout_behavior=".ui.behaviour.TestBehavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="#menu/bottom_nav_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
And this is the kotlin code:
class HomeActivity : AppCompatActivity() {
private lateinit var binding: ActivityHomeBinding
private val viewModel: HomeActivityViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHomeBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.homeToolbar)
val navView: BottomNavigationView = binding.navView
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment_activity_home) as NavHostFragment
val navController = navHostFragment.navController
// 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_home,
R.id.navigation_fragment_a,
R.id.navigation_notifications
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
}
This is the action xml:
<action
android:id="#+id/navigate_to_fragment_b"
app:destination="#id/fragment_b"
app:enterAnim="#anim/slide_in_right"
app:exitAnim="#anim/slide_out_left"
app:popEnterAnim="#anim/slide_in_right"
app:popExitAnim="#anim/slide_out_left" />
This the code I use to obtain the navController reference:
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment_activity_home) as NavHostFragment
val navController = navHostFragment.navController
And this is the navigation function that i call from the fragment_A:
navController.navigate(R.id.navigate_to_fragment_b)
I tried to override the onBackPressed() in my activity but it's never gets called.
Update
I have confused the back button with the up button.
In my case back button returns to previous fragment but up button close the app.
So my problem is with the UP BUTTON.
UP button appears only when I navigate to the fragment_B, and referring to this I would say that there is something that does not work because it closes the app for me.
The Up button never exits your app
If a user is at the app's start destination, then the Up button does not appear, because the Up button never exits the app. The Back button, however, is shown and does exit the app.
Thanks to ianhanniballake for the solution in this answer
// Java
#Override
public boolean onSupportNavigateUp() {
return Navigation.findNavController(this, R.id.main_fragment).navigateUp()|| super.onSupportNavigateUp();
}
// Kotlin
override fun onSupportNavigateUp(): Boolean {
return Navigation.findNavController(this, R.id.main_fragment).navigateUp() || super.onSupportNavigateUp()
}

Android Navigation goes back to title screen

My app right now has three screens: Home, Rockets and Company. When I open the app, it's on Home screen and there's a hamburger icon that opens the Navigation drawer. The problem is that when I select Rockets or Company from the drawer, the hamburger icon is replaced with a back arrow. Clicking on the arrow will navigate back to the Home screen. The hamburger icon reappears when I'm on Home screen.
activity_main.xml:
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar_main"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/midnight_blue" />
<androidx.fragment.app.FragmentContainerView
android:id="#+id/fragment_nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph" />
</LinearLayout>
<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"
app:menu="#menu/nav_menu"
/>
</androidx.drawerlayout.widget.DrawerLayout>
nav_menu.xml:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/homeFragment"
android:icon="#drawable/ic_home"
android:title="#string/nav_menu_home"
/>
<group
android:id="#+id/group_data"
>
<item
android:id="#+id/rocketsDataFragment"
android:icon="#drawable/ic_rocket"
android:title="#string/nav_menu_rockets_data"
/>
<item
android:id="#+id/companyDataFragment"
android:icon="#drawable/ic_company"
android:title="#string/nav_menu_company_data"
/>
</group>
<group
android:id="#+id/group_launches"
>
<item
android:id="#+id/past_launches"
android:icon="#drawable/ic_prev"
android:title="#string/nav_menu_launches_past"
/>
<item
android:id="#+id/upcoming_launches"
android:icon="#drawable/ic_next"
android:title="#string/nav_menu_launches_upcoming"
/>
</group>
</menu>
nav_graph.xml
<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/homeFragment"
>
<fragment
android:id="#+id/homeFragment"
android:name="com.example.rocketman.home.HomeFragment"
android:label="#string/nav_menu_home"
tools:layout="#layout/fragment_home"
/>
<fragment
android:id="#+id/rocketsDataFragment"
android:name="com.example.rocketman.rocket.RocketsDataFragment"
android:label="#string/nav_menu_rockets_data"
tools:layout="#layout/fragment_rockets_data"
/>
<fragment
android:id="#+id/companyDataFragment"
android:name="com.example.rocketman.company.CompanyDataFragment"
android:label="#string/nav_menu_company_data"
tools:layout="#layout/fragment_company_data"
/>
</navigation>
And finally, MainActivity:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val navController by lazy {
(supportFragmentManager
.findFragmentById(R.id.fragment_nav_host) as NavHostFragment).navController
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbarMain)
setupNavigationDrawer()
}
private fun setupNavigationDrawer() {
navController.let { navController ->
NavigationUI.setupActionBarWithNavController(
this,
navController,
binding.drawerLayout
)
NavigationUI.setupWithNavController(binding.navView, navController)
}
}
override fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(navController, binding.drawerLayout)
}
override fun onBackPressed() {
if(binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.drawerLayout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
}
So how can I make my screens have access to the Navigation Drawer instead of being navigation paths of Home screen?
In other words, I want my screen to all be able to go to another screen through Navigation drawer instead of going from that screen to Home screen and then go to another screen.
It seems like you didn't use AppBarConfiguration in your code which will resolve your problem.
AppBarConfiguration:
NavigationUI uses an AppBarConfiguration object to manage the behavior of the Navigation button in the upper-left corner of your app's display area. The Navigation button’s behavior changes depending on whether the user is at a top-level destination.
See the doc's for more understanding Update UI components with NavigationUI
I have updated the code of your Activity below. If Navigation Extensions Functions show's error makes sure to have these dependencies in your build.gradle file.
Dependencies:
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.3'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.3'
Updated Activity Code:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var appBarConfiguration: AppBarConfiguration
private val navController by lazy {
(supportFragmentManager
.findFragmentById(R.id.fragment_nav_host) as NavHostFragment).navController
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbarMain)
setupNavigationDrawer()
}
private fun setupNavigationDrawer() {
navController.let { navController ->
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
appBarConfiguration = AppBarConfiguration(setOf(
R.id.homeFragment, R.id.rocketsDataFragment, R.id.companyDataFragment), binding.drawerLayout)
setupActionBarWithNavController(navController, appBarConfiguration)
binding.navView.setupWithNavController(navController)
}
}
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
override fun onBackPressed() {
if(binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.drawerLayout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
}
NOTE: If you're using Android Studio version 4.1.2 by creating a new project (Navigation Drawer Activity Template) it will setup the project with AppBarConfiguration with all needed dependencies. So you can also try that.

navigation drawer with navigation component back arrow not show when drawer is open [duplicate]

I'm having some trouble to add options menu on a single fragment because it's breaking the navigation Up. Here my code
I have an single Activity with NoActionBar style and with this layout
<layout 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:fitsSystemWindows="true"
tools:context=".ui.MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white">
<fragment
android:id="#+id/mainNavigationFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/main_graph" />
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbarLayout"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="top">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:labelVisibilityMode="labeled"
app:menu="#menu/main_bottom_nav" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
in activity onCreate I make this setup for navigation
private fun setupNavigation() {
val navController = findNavController(R.id.mainNavigationFragment)
//each fragment of botton nav
val appBarConfiguration = AppBarConfiguration(setOf(
R.id.actionSchedule,
R.id.actionPayment,
R.id.actionNotification,
R.id.actionAccount))
toolbar.setupWithNavController(navController, appBarConfiguration)
bottomNavigationView.setupWithNavController(navController)
}
override fun onSupportNavigateUp() =
findNavController(R.id.mainNavigationFragment).navigateUp()
on each botton nav fragment I have some destinations and everything work as expected.
Now I need to add an menu only on right most fragment of botton nav, then on this specific fragment I add setHasOptionsMenu(true) in onCreate and inflate menu in onCreateOptionsMenu, but the menu does not appear.
Then I add setSupportActionBar(toolbar) on activity onCreate.
Now the menu appear only on this fragment but it's broke all 'UP' (back arrow on toolbar) of any destination (the back arrow appears, but when i press nothing happens). If I remove setSupportActionBar(toolbar) of activity the UP work again but not the toolbar menu.
What I need to do to make the menu work only in one fragment without broke anything else?
Thanks
If you're using setSupportActionBar, you must use setupActionBarWithNavController(), not toolbar.setupWithNavController as per the documentation.
if using toolbar do this:
in Acitvity:
class MyActivity : AppCompatActivity() {
private var currentNavController: NavController? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my)
currentNavController = findNavController(R.id.settingNavHost)
currentNavController?.let {
val appBarConfiguration = AppBarConfiguration
.Builder()
.setFallbackOnNavigateUpListener {
onBackPressed()
true
}.build()
setSupportActionBar(toolbar)
toolbar.setupWithNavController(it, appBarConfiguration)
}
}
}
and in fragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.my_menu, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.action_save) {
saveInfo()
}
return super.onOptionsItemSelected(item)
}

Add back arrow to fragment with Navigation Component

I implemented a basic Navigation component on an app that consist of a MainActivity that holds a Toolbar (that I have added to have the back arrow functionality) and a fragment container which starts with Fragment A. In this fragment I have a button which redirects to a blank Fragment B.
I can return to fragment A (from fragment B) from the bottom Android navigation using Navigation component, but I want to do the same using the back arrow from the toolbar that I have added.
I implemented the arrow putting setSupportActionBar(findViewById(R.id.toolbar)) in Main Activity and (activity as AppCompatActivity).supportActionBar?.setDisplayHomeAsUpEnabled(true) on Fragment B, but when I tap it it doesn't redirect to Fragment A because (I suppose) that the back stack is empty.
MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(findViewById(R.id.toolbar))
}
MainActivity xml
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:minHeight="?attr/actionBarSize"
android:theme="?attr/actionBarTheme"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView
android:id="#+id/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_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/toolbar"
app:navGraph="#navigation/nav_graph" />
FragmentB
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(activity as AppCompatActivity).supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
Added this to remove the default toolbar in styles.xml
<item name="windowNoTitle">true</item>
In FragmentB up button not working
fragmentB with back not working
I want to do that using Navigation Component, any ideas?
Thanks.
Thanks to #MustafaKhaled I could find the correct documentation that is in this link
I deleted setDisplayHomeAsUpEnabled of fragmentB (you don't need to add anything in this fragment).
I leave as it is the code of styles.xml.
My new MainActivity is like this:
private lateinit var toolbar: Toolbar
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
toolbar = findViewById(R.id.toolbar)
setupNavigation()
}
private fun setupNavigation() {
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
val appBarConfiguration = AppBarConfiguration.Builder(navController.graph).build()
toolbar.setupWithNavController(navController, appBarConfiguration)
}
Following this way you manage the toolbar behavior through the Navigation-UI feature.
UPDATE 28/04/20
There is an approach simpler than this that doesn't need a toolbar because uses the default one.
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)
setupNavigation()
}
private fun setupNavigation() {
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
appBarConfiguration = AppBarConfiguration.Builder(navController.graph).build()
setupActionBarWithNavController(navController, appBarConfiguration)
}
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration)
|| super.onSupportNavigateUp()
}
}
activity_main.xml
<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:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="#+id/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_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/nav_graph" />
And my styles.xml file is like the default one:
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
</style>
The updated repo link
In your MainActivity
setup your navigation:
private fun setupNavigation() {
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment?
navController = navHostFragment!!.navController
appBarConfiguration = AppBarConfiguration.Builder(setOf(R.id.checklocation,R.id.questions,R.id.news,R.id.profile_tab)).build()
NavigationUI.setupWithNavController(bottomNavigation,navController)
NavigationUI.setupActionBarWithNavController(this,navController)
}
to enable back arrow be shown and shows the previous destination override this function
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp()
}
By the way, using navigation component with bottom navigation, we don't have to configure your toolbar, this will be handled by adding above dunctions in your MainActivity.

How to properly add options menu on a single fragment with navigation component without broke "up behavior"

I'm having some trouble to add options menu on a single fragment because it's breaking the navigation Up. Here my code
I have an single Activity with NoActionBar style and with this layout
<layout 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:fitsSystemWindows="true"
tools:context=".ui.MainActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/white">
<fragment
android:id="#+id/mainNavigationFragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/main_graph" />
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbarLayout"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="top">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" />
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:labelVisibilityMode="labeled"
app:menu="#menu/main_bottom_nav" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
in activity onCreate I make this setup for navigation
private fun setupNavigation() {
val navController = findNavController(R.id.mainNavigationFragment)
//each fragment of botton nav
val appBarConfiguration = AppBarConfiguration(setOf(
R.id.actionSchedule,
R.id.actionPayment,
R.id.actionNotification,
R.id.actionAccount))
toolbar.setupWithNavController(navController, appBarConfiguration)
bottomNavigationView.setupWithNavController(navController)
}
override fun onSupportNavigateUp() =
findNavController(R.id.mainNavigationFragment).navigateUp()
on each botton nav fragment I have some destinations and everything work as expected.
Now I need to add an menu only on right most fragment of botton nav, then on this specific fragment I add setHasOptionsMenu(true) in onCreate and inflate menu in onCreateOptionsMenu, but the menu does not appear.
Then I add setSupportActionBar(toolbar) on activity onCreate.
Now the menu appear only on this fragment but it's broke all 'UP' (back arrow on toolbar) of any destination (the back arrow appears, but when i press nothing happens). If I remove setSupportActionBar(toolbar) of activity the UP work again but not the toolbar menu.
What I need to do to make the menu work only in one fragment without broke anything else?
Thanks
If you're using setSupportActionBar, you must use setupActionBarWithNavController(), not toolbar.setupWithNavController as per the documentation.
if using toolbar do this:
in Acitvity:
class MyActivity : AppCompatActivity() {
private var currentNavController: NavController? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my)
currentNavController = findNavController(R.id.settingNavHost)
currentNavController?.let {
val appBarConfiguration = AppBarConfiguration
.Builder()
.setFallbackOnNavigateUpListener {
onBackPressed()
true
}.build()
setSupportActionBar(toolbar)
toolbar.setupWithNavController(it, appBarConfiguration)
}
}
}
and in fragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.my_menu, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.action_save) {
saveInfo()
}
return super.onOptionsItemSelected(item)
}

Categories

Resources