I'm having an issue with a very simple task with Navigation component.
I have just 2 screens: MainFragment and SearchFragment.
When I try to go from MainFragment to SearchFragment by navigation with an action it works perfectly. Then I press the back button and naturally it goes back to the MainFragment.
The issue is, when I click the same button the second time to go again to the SearchFragment, I receive the following error:
java.lang.IllegalArgumentException: Navigation action/destination action_mainFragment_to_searchFragment cannot be found from the current destination Destination(searchFragment)
I'm navigating from the MainFragment to Search like this:
findNavController().navigate(MainFragmentDirections.actionMainFragmentToSearchFragment())
I tried redoing the nav_graph.xml but without success.
If I just navigate with the id directly, it works fine and I go back and forth as many times as I want
findNavController().navigate(R.id.searchFragment)
Any ideas how to fix the issue with the safe args?
Edit:
This is 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/nav_graph_main"
app:startDestination="#id/mainFragment">
<fragment
android:id="#+id/mainFragment"
android:name="news.presentation.main.MainFragment"
android:label="fragment_main"
tools:layout="#layout/fragment_main">
<action
android:id="#+id/action_mainFragment_to_searchFragment"
app:destination="#id/searchFragment" />
</fragment>
<fragment
android:id="#+id/searchFragment"
android:name="news.presentation.search.SearchFragment"
android:label="fragment_search"
tools:layout="#layout/fragment_search" />
</navigation>
This is the activity (it's basically just a container for the fragments):
This is the activity_home.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView 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/homeFragContainer"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph_main"
tools:context=".presentation.HomeActivity" />
And this is the HomeActivity.kt
#AndroidEntryPoint
class HomeActivity : AppCompatActivity(R.layout.activity_home)
This is the HomeFragment:
#AndroidEntryPoint
class MainFragment : Fragment(R.layout.fragment_main) {
private val binding by viewBinding(FragmentMainBinding::bind)
private val viewModel by viewModels<MainViewModel>()
private val articlesAdapter = ArticlesAdapter(::onSubscriptionClicked)
private lateinit var layoutManager: LinearLayoutManager
override fun onViewCreated(view: View, bundle: Bundle?) {
super.onViewCreated(view, bundle)
setupViews()
setupViewModel()
setupRecyclerView()
}
private fun setupViews() {
binding.toolbar.title = getString(R.string.app_name)
binding.fab.setOnClickListener {
viewModel.intent.offer(MainViewModel.Intent.SearchClicked)
}
}
private fun setupViewModel() {
viewModel.state
.onEach(::handleState)
.launchIn(lifecycleScope)
viewModel.feedFlow
.onEach(articlesAdapter::submitList)
.launchIn(lifecycleScope)
}
private fun setupRecyclerView() {
layoutManager = LinearLayoutManager(requireContext())
binding.recycler.layoutManager = layoutManager
binding.recycler.adapter = articlesAdapter
binding.recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
val canScroll = recyclerView.canScrollVertically(-1)
binding.toolbar.isLifted = canScroll
}
})
}
private fun onSubscriptionClicked(article: Article) {
viewModel.intent.offer(MainViewModel.Intent.ItemClicked(article))
}
private fun handleState(state: MainViewModel.State) = when (state) {
NavigateToSearch -> findNavController().navigate(MainFragmentDirections.actionMainFragmentToSearchFragment())
is FeedReady -> binding.progress.isVisible = false
Loading -> binding.progress.isVisible = true
is NavigateToArticle -> {
// works
// findNavController().navigate(
// R.id.articleFragment,
// bundleOf("articleLink" to state.link)
// )
}
}
}
And this is the XML for it:
<?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"
android:clipChildren="false"
android:clipToPadding="false"
tools:context=".presentation.main.MainFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#id/bottomNav"
app:layout_constraintTop_toBottomOf="#id/toolbar"
tools:listitem="#layout/item_article_big" />
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/standard"
android:text="Add Feed"
android:textAlignment="center"
app:icon="#drawable/ic_add"
app:layout_constraintBottom_toTopOf="#id/bottomNav"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="#menu/menu_main" />
<tgo1014.news.presentation.customview.LiftableToolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="#+id/progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
app:layout_constraintBottom_toBottomOf="#id/toolbar"
app:layout_constraintTop_toBottomOf="#id/toolbar"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>
the error is due to the lifecycle.
Replace this:
viewModel.state
.onEach(::handleState)
.launchIn(lifecycleScope)
by
viewModel.state
.onEach(::handleState)
.launchIn(viewLifecycleOwner.lifecycleScope)
Related
Take a Bottom Navigation activity from android studio template. there are 3 fragment with 3 item in BottomNavBar (HomeFragment, DashboardFragment, NotificationsFragment) navigate to DashboardFragment from HomeFragment by a button click. after that home item click from BottomNavBar should open Homefragment. But not working as expected.
Go to DashboardFrament from HomeFrament by
textView.setOnClickListener {
findNavController().navigate(R.id.navigation_dashboard)
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navView: BottomNavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_activity_main)
navView.setupWithNavController(navController)
}
}
main_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"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize">
<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_nav_menu" />
<fragment
android:id="#+id/nav_host_fragment_activity_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
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/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
HomeFramgent.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val textView: TextView = view.findViewById(R.id.text_home)
textView.setOnClickListener {
findNavController().navigate(R.id.navigation_dashboard)
}
}
navigation.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/mobile_navigation"
app:startDestination="#+id/navigation_home">
<fragment
android:id="#+id/navigation_home"
android:name="com.app.bottomnav.ui.home.HomeFragment"
android:label="#string/title_home"
tools:layout="#layout/fragment_home" />
<fragment
android:id="#+id/navigation_dashboard"
android:name="com.app.bottomnav.ui.dashboard.DashboardFragment"
android:label="#string/title_dashboard"
tools:layout="#layout/fragment_dashboard" />
<fragment
android:id="#+id/navigation_notifications"
android:name="com.app.bottomnav.ui.notifications.NotificationsFragment"
android:label="#string/title_notifications"
tools:layout="#layout/fragment_notifications" />
</navigation>
Whenever you are navigating to any root destination from another fragment, you should have to clear the previous stack using popUpTo option builder.
Update your code navigation code in your home fragment.
findNavController()
.navigate(R.id.navigation_dashboard,
null,
NavOptions.Builder()
.setPopUpTo(R.id.navigation_home, true)
.build()
)
I don't know what is the best solution for that problem but here is a workaround:
In your activity you add public method:
fun navigateToNavBarDestination(destinationId: Int) {
binding.navView.setSelectedItemId(destinationId)
}
And in your fragments referenced in BottomNavigationView instead calling NavController::navigate you navigate in that way, lets say to dashboard:
fun navigateToDashboard() {
(activity as? MainActivity)?.navigateToNavBarDestination(R.id.dashboard)
}
Its not the perfect solution but works.
Add these lines in your MainActivity onCreate.
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home,
R.id.navigation_dashboard,
R.id.navigation_notifications
))
setupActionBarWithNavController(navController, appBarConfiguration)
Im having a problem on my navigation, first i have an A fragment which has a recycler view, when i use the navigation to move to another fragment (B fragment) and come back to my A fragment i cant seem to be able to click on the item contained in the recycler view, it raises me "View androidx.constraintlayout.widget.ConstraintLayout{1f8e1b2 V.E...... ........ 0,0-1080,1823} does not have a NavController set". If i dont move in beetween fragments (stay in A fragment) it works just fine.
The onItemClicked on my adapter calls a findNavController().
ps: Sry for my bad english as im not native, and this is my first post
This is my MainActivity:
`
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainMenuBinding
private lateinit var bottomNav: BottomNavigationView
private lateinit var navController: NavController
val authViewModel: AuthViewModel by viewModels()
val TAG: String = "ReceitaListingFragment"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startUI()
}
private fun setNavController(){
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host) as NavHostFragment
navController = navHostFragment.navController
setupWithNavController(bottomNav,navController)
}
private fun startUI() {
binding = ActivityMainMenuBinding.inflate(layoutInflater)
setContentView(binding.root)
bottomNav = findViewById(R.id.bottomNavigationView)
setNavController()
bottomNav.setOnItemSelectedListener {
when (it.itemId){
R.id.recipes -> {
val connected_to_internet:Boolean = isOnline(this)
val fragment:Fragment = RecipeListingFragment()
fragment.arguments = Bundle().apply {
putBoolean("connectivity",connected_to_internet)
}
replaceFragment(RecipeListingFragment())
}
R.id.profile -> replaceFragment(ProfileFragment())
}
true
}
}
private fun replaceFragment(fragment : Fragment){
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.replace(R.id.nav_host,fragment)
fragmentTransaction.commit()
}
}
`
this is my main_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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="#+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/app_navigation" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#E3E3E4"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:itemIconTint="#color/bordeux"
app:itemTextColor="#color/bordeux"
app:menu="#menu/bottom_nav_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
`
this is how i initialize my adapter in RecipeListingFragment:
`
private val adapter by lazy {
RecipeListingAdapter(
onItemClicked = {pos,item ->
findNavController().navigate(R.id.action_receitaListingFragment_to_receitaDetailFragment,Bundle().apply {
putParcelable("note",item)
})
},
this.authModel
)
}
`
this is how i set my adapter in RecipeListingFragment:
`
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.recyclerView.adapter = adapter
}
`
this is my navigation.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/app_navigation"
app:startDestination="#id/receitaListingFragment">
<fragment
android:id="#+id/receitaListingFragment"
android:name="com.example.projectfoodmanager.ui.recipe.RecipeListingFragment"
android:label="fragment_receita_listing"
tools:layout="#layout/fragment_recipe_listing" >
<action
android:id="#+id/action_receitaListingFragment_to_receitaDetailFragment"
app:destination="#id/receitaDetailFragment" />
<action
android:id="#+id/action_receitaListingFragment_to_profileFragment"
app:destination="#id/profileFragment" />
</fragment>
<fragment
android:id="#+id/receitaDetailFragment"
android:name="com.example.projectfoodmanager.ui.recipe.RecipeDetailFragment"
android:label="fragment_recipe_detail"
tools:layout="#layout/fragment_recipe_detail" >
<action
android:id="#+id/action_receitaDetailFragment_to_receitaListingFragment"
app:destination="#id/receitaListingFragment" />
</fragment>
<fragment
android:id="#+id/profileFragment"
android:name="com.example.projectfoodmanager.ui.profile.ProfileFragment"
android:label="fragment_profile"
tools:layout="#layout/fragment_profile" >
<action
android:id="#+id/action_profileFragment_to_receitaListingFragment"
app:destination="#id/receitaListingFragment" />
</fragment>
<fragment
android:id="#+id/calenderFragment"
android:name="com.example.projectfoodmanager.ui.views.CalenderFragment"
android:label="fragment_calender"
tools:layout="#layout/fragment_calender" >
</fragment>
<fragment
android:id="#+id/favoritesFragment"
android:name="com.example.projectfoodmanager.ui.views.FavoritesFragment"
android:label="fragment_favorites"
tools:layout="#layout/fragment_favorites" >
</fragment>
<fragment
android:id="#+id/goalFragment"
android:name="com.example.projectfoodmanager.ui.views.GoalFragment"
android:label="fragment_goal"
tools:layout="#layout/fragment_goal" >
</fragment>
</navigation>
`
Try to using RecyclerView and move fragment navigation to your Fragment.
Your Adapter:
class MyCustomAdapter(
val openFragment: () -> Unit
) :
RecyclerView.Adapter<PromoAdapter.UserViewHolder>() {
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.button.setOnClickListener {
openFragment()
}
}
}
In Fragment:
val mAdapter = MyCustomAdapter(
openFragment = {
// Navigate to Fragment
}
)
I have already looked this question: FragmentContainerView using findNavController about this problem. But however I stiil couldn't solve the problem. It never opens other fragments. I thought maybe it is because of view binding but I tried to do it again without using view binding. It doesn't open still.
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var navController: NavController
private lateinit var navHostFragment: NavHostFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.main_nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
binding.bottomNavigationView.setupWithNavController(navController)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.home -> {
navHostFragment.navController.popBackStack()
return true
}
}
return super.onOptionsItemSelected(item)
}
}
activity_main.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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/main_nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph_main" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="56dp"
app:menu="#menu/menu"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
nav_graph_main
<?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_main"
app:startDestination="#id/fragmentLibrary">
<fragment
android:id="#+id/fragmentLibrary"
android:name="com.project.biosec.FragmentLibrary"
android:label="fragment_library"
tools:layout="#layout/fragment_library" />
<fragment
android:id="#+id/fragmentTransaction"
android:name="com.project.biosec.FragmentTransaction"
android:label="fragment_transaction"
tools:layout="#layout/fragment_transaction" />
<fragment
android:id="#+id/fragmentAccount"
android:name="com.project.biosec.FragmentAccount"
android:label="fragment_account"
tools:layout="#layout/fragment_account" >
<action
android:id="#+id/action_fragmentAccount_to_changeSignatureFragment"
app:destination="#id/changeSignatureFragment" />
<action
android:id="#+id/action_fragmentAccount_to_securityFragment"
app:destination="#id/securityFragment" />
<action
android:id="#+id/action_fragmentAccount_to_helpFragment"
app:destination="#id/helpFragment" />
<action
android:id="#+id/action_fragmentAccount_to_termsFragment"
app:destination="#id/termsFragment" />
</fragment>
<fragment
android:id="#+id/changeSignatureFragment"
android:name="com.project.biosec.ChangeSignatureFragment"
android:label="fragment_change_signature"
tools:layout="#layout/fragment_change_signature" />
<fragment
android:id="#+id/securityFragment"
android:name="com.project.biosec.SecurityFragment"
android:label="fragment_security"
tools:layout="#layout/fragment_security" />
<fragment
android:id="#+id/helpFragment"
android:name="com.project.biosec.HelpFragment"
android:label="fragment_help"
tools:layout="#layout/fragment_help" />
<fragment
android:id="#+id/termsFragment"
android:name="com.project.biosec.TermsFragment"
android:label="fragment_terms"
tools:layout="#layout/fragment_terms" />
</navigation>
menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/feed_fragment"
android:icon="#drawable/ic_library"
android:title="#string/library" />
<item
android:id="#+id/messages_fragment"
android:icon="#drawable/ic_assignment"
android:title="#string/transactions" />
<item
android:id="#+id/profile_fragment"
android:icon="#drawable/ic_account"
android:title="#string/account" />
</menu>
As per the Setting up bottom navigation guide:
Note: Setting up bottom navigation requires that you also set up your navigation graph and menu xml as described in Tie destinations to menu items.
That section specifically states that the android:id of your destination in your navigation graph XML file needs to match the android:id of the menu item in your menu XML file.
In your case, your navigation XML uses android:id="#+id/fragmentLibrary", android:id="#+id/fragmentTransaction", and android:id="#+id/fragmentAccount", so your menu items should change to use those same IDs.
Good morning, any recommendations to use, navigation componets: BottomNavigationView and NavigationDrawer, In a project android.
This project was based on the Samples for Android Architecture Components repository. This is the repository where I took it out:
https://github.com/mackgaru/architecture-components-samples/tree/main/NavigationAdvancedSample
When clicking on a NavigationDrawer item, the navigation does not work well or the app crashes.
This is my repo, if you want to give me a hand:
https://github.com/mackgaru/nBottom_nDrawer.git
Thank you so much.
BottonNavigationView, NavigationDrawer
Here is the error
Process: com.cocktapp.cocktapp, PID: 27899
java.lang.IllegalArgumentException: Navigation action/destination com.cocktapp.cocktapp:id/action_navigation_home_to_homeDetalleFragment cannot be found from the current destination Destination(com.cocktapp.cocktapp:id/homeDetalleFragment) label=Detalle class=com.cocktapp.cocktapp.ui.home.HomeDetalleFragment
at androidx.navigation.NavController.navigate(NavController.java:938)
at androidx.navigation.NavController.navigate(NavController.java:875)
at androidx.navigation.NavController.navigate(NavController.java:861)
at androidx.navigation.NavController.navigate(NavController.java:849)
at com.cocktapp.cocktapp.ui.MainActivity.onNavigationItemSelected(MainActivity.kt:95)
at com.google.android.material.navigation.NavigationView$1.onMenuItemSelected(NavigationView.java:217)
at androidx.appcompat.view.menu.MenuBuilder.dispatchMenuItemSelected(MenuBuilder.java:834)
at androidx.appcompat.view.menu.MenuItemImpl.invoke(MenuItemImpl.java:158)
at androidx.appcompat.view.menu.MenuBuilder.performItemAction(MenuBuilder.java:985)
at com.google.android.material.internal.NavigationMenuPresenter$1.onClick(NavigationMenuPresenter.java:416)
at android.view.View.performClick(View.java:5646)
at android.view.View$PerformClick.run(View.java:22473)
at android.os.Handler.handleCallback(Handler.java:761)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:156)
at android.app.ActivityThread.main(ActivityThread.java:6517)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:942)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:832)
MainActivity
class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
private var currentNavController: LiveData<NavController>? = null
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityMainBinding
private lateinit var drawerLayout: DrawerLayout
private lateinit var viewModelHome: HomeViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
if (savedInstanceState == null) {
setupBottomNavigationBar()
}
setSupportActionBar(binding.includeMainContent.toolbarActivity)
drawerLayout = binding.drawerLayout
val navigationView: NavigationView = binding.navView
val toogle = ActionBarDrawerToggle(this, drawerLayout, binding.includeMainContent.toolbarActivity, R.string.open_navigation, R.string.close_navigation)
drawerLayout.addDrawerListener(toogle)
toogle.syncState()
navigationView.setNavigationItemSelectedListener(this)
}//fin onCreate
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
setupBottomNavigationBar()
}
private fun setupBottomNavigationBar() {
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_nav)
val navGraphIds = listOf(R.navigation.navigation_home, R.navigation.navigation_add, R.navigation.navigation_card, R.navigation.navigation_pay, R.navigation.navigation_config)
// Setup the bottom navigation view with a list of navigation graphs
val controller = bottomNavigationView.setupWithNavController(
navGraphIds = navGraphIds,
fragmentManager = supportFragmentManager,
containerId = R.id.nav_host_container,
intent = intent
)
// Whenever the selected controller changes, setup the action bar
controller.observe(this, Observer { navController ->
setupActionBarWithNavController(navController)
})
currentNavController = controller
Log.i("controller", "valor es = ${currentNavController!!.value}");
}
override fun onSupportNavigateUp(): Boolean {
Log.i("controller", "support = ${currentNavController?.value?.navigateUp()}");
val navController = findNavController(R.id.nav_host_container)
//return currentNavController?.value?.navigateUp() ?: false
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return super.onOptionsItemSelected(item)
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
if(item.itemId == R.id.nav_camera){
Toast.makeText(this , "Camera Click", Toast.LENGTH_SHORT).show()
//Navigation.findNavController(this, R.id.navigationHome).navigate(R.id.action_navigation_home_to_homeDetalleFragment)
currentNavController?.value?.navigate(R.id.action_navigation_home_to_homeDetalleFragment)
drawerLayout.closeDrawer(Gravity.LEFT)
}
return true;
}
override fun onBackPressed() {
if(drawerLayout.isDrawerOpen(Gravity.LEFT)){
drawerLayout.closeDrawer(Gravity.LEFT)
}else{
super.onBackPressed()
}
}
}
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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/drawer_layout"
android:fitsSystemWindows="true"
tools:openDrawer="start">
<include
android:id="#+id/include_main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
layout="#layout/dra_main_content_appbar"/>
<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/dra_drawer_nav_header"
app:menu="#menu/drawer_navigation" />
</androidx.drawerlayout.widget.DrawerLayout>
dra_main_content_appbar
<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=".ui.MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/Theme.Cocktapp.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar_activity"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/Theme.Cocktapp.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<include layout="#layout/dra_content" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
dra_content
<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"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:showIn="#layout/dra_main_content_appbar">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host_container"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottom_nav"
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_nav_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
Each element of the BottonNavigationView has a different navigation file: example: navigation_home
<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/navigationHome"
app:startDestination="#id/navigation_home">
<fragment
android:id="#+id/navigation_home"
android:name="com.cocktapp.cocktapp.ui.home.HomeFragment"
android:label="Home"
tools:layout="#layout/fragment_home" >
<action
android:id="#+id/action_navigation_home_to_homeDetalleFragment"
app:destination="#id/homeDetalleFragment" />
</fragment>
<fragment
android:id="#+id/homeDetalleFragment"
android:name="com.cocktapp.cocktapp.ui.home.HomeDetalleFragment"
android:label="Detalle"
tools:layout="#layout/fragment_home_detalle" />
</navigation>
example: navigation_add
<?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/navigationAdd"
app:startDestination="#id/navigation_add">
<fragment
android:id="#+id/navigation_add"
android:name="com.cocktapp.cocktapp.ui.add.AddFragment"
android:label="Nuevo"
tools:layout="#layout/fragment_add" >
<action
android:id="#+id/action_navigation_add_to_addDetallekFragment"
app:destination="#id/addDetalleFragment" />
</fragment>
<fragment
android:id="#+id/addDetalleFragment"
android:name="com.cocktapp.cocktapp.ui.add.AddDetalleFragment"
android:label="Detalle"
tools:layout="#layout/fragment_add_detalle" />
</navigation>
All you have to do is:
override fun onNavigationItemSelected(item: MenuItem): Boolean {
if(item.itemId == R.id.nav_camera){
Toast.makeText(this , "Camera Click", Toast.LENGTH_SHORT).show()
if(currentNavController.currentDestination.id != R.id.homeDetalleFragment){
currentNavController?.value?.navigate(R.id.action_navigation_home_to_homeDetalleFragment)
}
drawerLayout.closeDrawer(Gravity.LEFT)
}
return true;
}
I am using jetpack navigation in two different activities each with its navigation host. I have Main activity which everything just works fine and Setup activity which have 2 fragments, one has a button which should navigate to other fragment when clicked .
The activity's code is as follows:
class SetupActivity : AppCompatActivity() {
private val navController by lazy { findNavController(R.id.nav_host_fragment) }
private lateinit var appBarConfig: AppBarConfiguration
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DataBindingUtil.setContentView<ActivitySetupBinding>(this, R.layout.activity_setup)
setSupportActionBar(toolbar)
appBarConfig = AppBarConfiguration(navController.graph)
setupActionBarWithNavController(navController, appBarConfig)
}
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfig) || super.onSupportNavigateUp()
}
}
Below is activity's 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:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".ui.SetupActivity"
tools:showIn="#layout/activity_setup">
<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"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/setup" />
</androidx.constraintlayout.widget.ConstraintLayout>
Below is code of first fragment which is also a start Destination
class ConfirmClassFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentConfirmClassBinding.inflate(inflater, container, false)
binding.fragment = this
return binding.root
}
fun navigate() {
toast("clicked") //this get called
val direction = ConfirmClassFragmentDirections.actionConfirmToSetup()
findNavController().navigate(direction) //THE PROBLEM IS HERE
//findNavController().navigate(R.id.setup)
}
}
I am using data binding to call fun navigate() as below:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="fragment"
type="com.nux.ui.fragments.ConfirmClassFragment" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="#dimen/spacing_middle">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="#{() -> fragment.navigate()}"
android:text="#string/continue_" />
</LinearLayout>
</layout>
and this how navigation xml looks like:
<?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/setup"
app:startDestination="#id/confirm">
<fragment
android:id="#+id/setup"
android:name="com.nux.ui.fragments.SetInfoFragment"
android:label="#string/setup"
tools:layout="#layout/fragment_set_info" />
<fragment
android:id="#+id/confirm"
android:name="com.nux.ui.fragments.ConfirmClassFragment"
android:label="#string/data_confirmation"
tools:layout="#layout/fragment_confirm_class">
<action
android:id="#+id/action_confirm_to_setup"
app:destination="#id/setup" />
</fragment>
</navigation>
When I click button fun gets called but nothing happens! What could be wrong?
From the code i can see you have the same id for 3 elements in the navigation graph.
#id/setup should only be used once and please change the name of the navigation graph from setup to something else.