I have this code in my BottomActivity.
private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
when (item.itemId) {
R.id.navigation_home -> {
Log.d("Navigation", "You are picking bottombar")
supportActionBar?.title = "Navigation Feed"
val dashboardFragment = DashboardFragment()
openFragment(dashboardFragment)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_dashboard -> {
val homeFragment = HomeFragment()
openFragment(homeFragment)
return#OnNavigationItemSelectedListener true
}
R.id.navigation_notifications -> {
val notificationFragment = NotificationsFragment()
openFragment(notificationFragment)
return#OnNavigationItemSelectedListener true
}
}
false
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_bottom)
val navView: BottomNavigationView = findViewById(R.id.nav_view)
navView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
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_home, R.id.navigation_dashboard, R.id.navigation_notifications))
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
private fun openFragment(fragment: Fragment) {
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.container, fragment)
transaction.addToBackStack(null)
transaction.commit()
}
All what it should do is setting listener and opening my fragments but it doesn't.
I think that problem is somewhere in mOnNavigationItemSelectedListener because
it even doesnt' log on console. My activity is loading just first fragment with text
This is home fragment and bottomview doesn't react on click. I need solution in kotlin language.
For navigation component to work correctly, you do not need BottomNavigationView.OnItemSelectedListener actually. In OnItemSelectedListener, loading fragments manually is exacly WRONG behaviour. If you really need it, you should do it in the following way:
bottomNav.setOnNavigationItemSelectedListener {
// ...
NavigationUI.onNavDestinationSelected(it, navController)
// ...
}
Check the ids of your menu items in menu_bottomnavigationview.xml.
Check the ids of your top level fragments in your navigation graphs, namely R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications.
These ids should be the same. Thats enough for navigation controller to load correct fragment into the NavHostFragment.
In addition to this, if you write BottomNavigationView.OnItemSelectedListener, then you overrided the default behaviour. Then navigation controller cannot load correct fragment into the NavHostFragment. If you really need to use BottomNavigationView.OnItemSelectedListener, then you should write the following code for loading fragments into NavHostFragment.
bottomNav.setOnNavigationItemSelectedListener {
NavigationUI.onNavDestinationSelected(it, navController)
}
Related
I'm developing a simple app, with several static screens, just. I get working passing from first fragment, but can't go back to any screen ever.
I saw some videos, like shorturl.at/jFPRY, but in any cases makes work. In my MainActivity I have the OnCreate method, where I save the instance, set the layout, and add a Listener to options on my menu itens.
In the first click, when I open the app, it goes to correct destiny. However, when I click another item it does nothing, stucked. I need to set the Fragments bidirectional, in other words, the user can view any fragment in any moment from any fragment.
My OnCreate method:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.appBarMain.toolbar)
val drawerLayout: DrawerLayout = binding.drawerLayout
val navView: NavigationView = binding.navView
navView.setNavigationItemSelectedListener {
when (it.itemId) {
R.id.nav_conheca_a_UFTM -> {
supportFragmentManager.beginTransaction()
.replace(R.id.drawer_layout, ConhecaUftmFragment())
.commit()
// Remove the other operations
supportFragmentManager.beginTransaction().addToBackStack(null);
}
R.id.nav_calendario_academico -> {
supportFragmentManager.beginTransaction()
.replace(R.id.drawer_layout, GalleryFragment())
.commit()
// Remove the other operations
supportFragmentManager.beginTransaction().addToBackStack(null);
}
R.id.nav_mural_de_informacoes -> {
supportFragmentManager.beginTransaction()
.replace(R.id.drawer_layout, CalendarioFragment())
.commit()
// Remove the other operations
supportFragmentManager.beginTransaction().addToBackStack(null);
}
}
it.isChecked = true
drawerLayout.closeDrawers()
true
}
supportActionBar?.apply {
setDisplayHomeAsUpEnabled(true)
}
navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main)
val appBarConfiguration = AppBarConfiguration(navController.graph, drawerLayout)
NavigationUI.setupWithNavController(binding.appBarMain.toolbar, navController, appBarConfiguration)
NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
}
I have also the method onSupportNavigateUp:
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(id.nav_host_fragment_content_main)
return navController.navigateUp()
}
And here is an example of Fragment that I have:
class GalleryFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_calendario_academico, container, false)
/* view.setOnClickListener(){ Navigation.findNavController(view).
navigate(R.id.conhecauftmToaprimorando)
}*/
return view
}
override fun onDestroyView() {
super.onDestroyView()
}
}
I will pass my GitHub link, if anyone can help to fix this issue.
First approach would be to not use FragmentTransactions when you using NavComponent.
If you have the MenuOptions on the MainActivity layout (which I don't recommend, it should go on every fragment, so the MenuOptions are correlated to the current fragment) you can define your navigations in the nav_graph.xml and use them in the MainActivity through NavigationDirections object.
Please visit https://developer.android.com/guide/navigation/navigation-getting-started for more info.
If you want to come back from GalleryFragment to where you have defined the destination of the Popback behaviour, you should call: findNavController.popBackStack()
I use Navigatation component latest stable version.
I would like to create a shortcut option menu item in my application, which is navigates to a fragment.
This fragment is also used by the drawer menu.
So when I use the drawer menu for navigation, everything is good, but when I navigate to for example Fragment B and after that I use the options menu to navigate to Fragment C, and after the I can not navigate back to Fragment B from drawer, because it always shows Fragment C.
I can solve this problem by adding navController.popBackStack() before the navigation to Fragment C, but this is not good at all, becaouse the previous fragment is destroyed, and I can not go back to it, by pressing back button.
Is there any solution of this problem?
MainActivity
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.menu_main,menu)
menu?.findItem(R.id.action_worksheet_management)?.setOnMenuItemClickListener {
val navController = findNavController(R.id.nav_host_fragment_content_main)
navController.popBackStack()
navController.navigate(R.id.nav_work)
return#setOnMenuItemClickListener true
}
return true
}
private fun setUpNavigation() {
val navController = findNavController(R.id.nav_host_fragment_content_main)
setSupportActionBar(binding.appBarMain.toolbar)
val menuItems = mutableListOf(
R.id.nav_worksheet_browser,
R.id.nav_product,
R.id.nav_about,
R.id.nav_company_info,
R.id.nav_services,
R.id.nav_stock_receive,
R.id.nav_synchronization,
R.id.nav_order_return,
R.id.nav_work,
R.id.nav_settings,
R.id.nav_certificates
)
appBarConfiguration = AppBarConfiguration(
menuItems.toSet(),
binding.drawerLayout
)
setupActionBarWithNavController(navController, appBarConfiguration)
binding.navView.setupWithNavController(navController)
}
override fun onBackPressed() {
if (binding.drawerLayout.isDrawerOpen(GravityCompat.START)) {
binding.drawerLayout.closeDrawer(GravityCompat.START)
} else {
super.onBackPressed()
}
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment_content_main)
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
I resolved it, by changed the nav version from 2.4.1 to 2.3.5. I hope it will be fixed later.
For navigation, I use the Navigation The component I connected to it using the Navigation Extension BottomNavigationView. I tried to switch from one graph to another, but he always showed me an error that he could not find a path where to go. If the way to go from one graph to another
the code where the Navigation Component and bottomNavView are connected
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private lateinit var appBarConfiguration: AppBarConfiguration
private var currentNavController: LiveData<NavController>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
if (savedInstanceState == null) {
initNavigationComponent()
}
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
initNavigationComponent()
}
override fun onSupportNavigateUp(): Boolean {
return currentNavController?.value?.navigateUp(appBarConfiguration) ?: false
|| super.onSupportNavigateUp()
}
private fun initNavigationComponent() {
appBarConfiguration =
AppBarConfiguration(setOf(R.id.auth_fragment, R.id.main_screen_fragment))
setSupportActionBar(toolbar)
val navGraphIds =
listOf(R.navigation.nav_main, R.navigation.nav_catalog, R.navigation.nav_profile)
// Setup the bottom navigation view with a list of navigation graphs
val controller = bottom_nav.setupWithNavController(
navGraphIds = navGraphIds,
fragmentManager = supportFragmentManager,
containerId = R.id.main_nav_fragment,
intent = intent
)
controller.observe(this, { navController ->
setupActionBarWithNavController(navController,appBarConfiguration)
collapsingToolbarLayout.setupWithNavController(
toolbar,
navController,
appBarConfiguration
)
navController.addOnDestinationChangedListener {...} )
currentNavController = controller
}
}
Tried connecting with <include app:graph = "#navigation/myGraph" /> but that doesn't work either
for multi backstack navigation between BottomNavigationView tabs use this sample of google:
Multiple BackStack Navigation
So, I use the latest version of androidstudio and kotlin.
I created a bottom navigation activity project, and I use the activity with the drefault generated code.
I would like to open/replace a fragment from another fragment by button.
But the previous fragment doesn't disappear, when the new fragment is appear, so both are visible.
I read a lot, but I don't know the solution, please help me with some advice.
This is my MainActivity
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)
// 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_advertisements, R.id.navigation_own_advertisements, R.id.navigation_chat, R.id.navigation_party, R.id.navigation_settings))
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
And this is my fragment
override fun onViewCreated(view: View, savedInstanceState: Bundle ? ) {
super.onViewCreated(view, savedInstanceState)
button.setOnClickListener {
activity!!.supportFragmentManager
.beginTransaction()
.replace(R.id.nav_host_fragment, ChatFragment())
.commit()
}
}
How can I add Fragment Change Listener in new Navigation Component?
I have a BottomNavigationView in which I used new Navigation Component following official sample
I have four destinations in my BottomNavigationView, all of them have their navigation graphs.
val navGraphIds = listOf(R.navigation.nav_home, R.navigation.nav_discover, R.navigation.nav_search, R.navigation.nav_my)
val controller = bottom_nav.setupWithNavController(
navGraphIds = navGraphIds,
fragmentManager = supportFragmentManager,
containerId = R.id.navHostContainer,
intent = intent
)
controller.observe(this, Observer { navController ->
setupActionBarWithNavController(navController)
})
I want to have a listener in my MainActivity when fragment changed in any of 4 navigation graphs.
the controller is only affective when switching between BottomNavigationView destinations.
Have you tried NavController.OnDestinationChangedListener?
private lateinit var controller: NavController // don't forget to initialize
private val listener = NavController.OnDestinationChangedListener { controller, destination, arguments ->
// react on change
// you can check destination.id or destination.label and act based on that
}
override fun onResume() {
super.onResume()
controller.addOnDestinationChangedListener(listener)
}
override fun onPause() {
controller.removeOnDestinationChangedListener(listener)
super.onPause()
}
Addition to marat and vlad answers , if u use FragmentContainer in Xml , you can access it's nav controller by this :
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
else may encounter IllegalStateException of acitivity dont have navcontroller