I'm starting to learn Kotlin for Android apps. This is one of my first projects and I'm trying to set a toggle listener for my drawer menu, but getting an error because it is being returned as null. The ID I'm using to find it seems to be set correctly.
activity_main.xml file:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:title="#string/app_name"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/rv_list"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintTop_toBottomOf="#+id/toolBar"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
The drawer_menu.xml file:
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/drawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:openDrawer="start" xmlns:app="http://schemas.android.com/apk/res-auto">
<include layout="#layout/activity_main"/>
<com.google.android.material.navigation.NavigationView
android:id="#+id/nav_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
app:menu="#menu/menu"/>
</androidx.drawerlayout.widget.DrawerLayout>
And the MainActivity.kt (where I'm setting the toggle)
class MainActivity : AppCompatActivity() {
private val rvList: RecyclerView by lazy {
findViewById<RecyclerView>(R.id.rv_list)
}
private val adapter = ContactAdapter()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initDrawer()
bindViews()
updateList()
}
private fun initDrawer() {
val drawerLayout = findViewById<View>(R.id.drawerLayout) as DrawerLayout
val toolBar = findViewById<View>(R.id.toolBar) as Toolbar
setSupportActionBar(toolBar)
val toggle = ActionBarDrawerToggle(this, drawerLayout, toolBar, R.string.open_drawer, R.string.close_drawer)
drawerLayout.addDrawerListener(toggle)
toggle.syncState()
}
...
}
And this is the error I'm getting:
2022-01-19 10:16:29.863 10567-10567/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: curso.android.contactlist, PID: 10567
java.lang.RuntimeException: Unable to start activity ComponentInfo{curso.android.contactlist/curso.android.contactlist.MainActivity}: java.lang.NullPointerException: null cannot be cast to non-null type androidx.drawerlayout.widget.DrawerLayout
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2861)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2943)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1630)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6626)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:811)
Caused by: java.lang.NullPointerException: null cannot be cast to non-null type androidx.drawerlayout.widget.DrawerLayout
at curso.android.contactlist.MainActivity.initDrawer(MainActivity.kt:34)
at curso.android.contactlist.MainActivity.onCreate(MainActivity.kt:27)
at android.app.Activity.performCreate(Activity.java:7032)
at android.app.Activity.performCreate(Activity.java:7023)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1236)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2814)
Does anyone know why is it returning null?
the drawer_menu isn't inside activity_main but the other way around. so replace
setContentView(R.layout.activity_main)
with
setContentView(R.layout.drawer_menu)
There might be more problems but this is what I noticed at first
Am i missing something, or is your Drawer layout not part of the activity_main.xml? If that is the case, then its logical why your findViewById<View>(R.id.drawerLayout) will return null.
Related
I'm trying to set up a Wear OS project. I have some experience with normal android development but Wear os is new for me.
I'm used to just having one MainActivity and some fragments. But I'm trying to make that work for Wear OS but with no success. It keeps crashing when I want to use viewBinding in the OverviewFragment.kt.
I got the following project now:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.wear.widget.BoxInsetLayout 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:padding="#dimen/box_inset_layout_padding"
tools:context=".MainActivity"
tools:deviceIds="wear">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="#dimen/inner_frame_layout_padding"
app:layout_boxedEdges="left|right">
<include
layout="#layout/content_main"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</androidx.wear.widget.BoxInsetLayout>
content_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.wear.widget.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<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_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="#navigation/nav_graph" />
</androidx.wear.widget.BoxInsetLayout>
fragment_overview.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.OverviewFragment">
<TextView
android:id="#+id/tvOverview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:text="overview"
android:textAlignment="center" />
</FrameLayout>
MainActivity.kt
class MainActivity : FragmentActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
}
OverviewFragment.kt
class OverviewFragment : Fragment() {
private var _binding: FragmentOverviewBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_overview, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.tvOverview.setOnClickListener {
println("great")
}
}
}
Just a simple design, but it does not like the binding.tvOverview.setOnClickListener part in OverviewFragment.kt. It just keeps crashing.
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.krakert.tracker, PID: 7261
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.krakert.tracker/com.krakert.tracker.MainActivity}: android.view.InflateException: Binary XML file line #17: Binary XML file line #8: Error inflating class fragment
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2817)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6541)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
Caused by: android.view.InflateException: Binary XML file line #17: Binary XML file line #8: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #8: Error inflating class fragment
Caused by: java.lang.NullPointerException
at com.krakert.tracker.ui.OverviewFragment.getBinding(OverviewFragment.kt:15)
at com.krakert.tracker.ui.OverviewFragment.onViewCreated(OverviewFragment.kt:28)
I would like some help here, setting the project up. Really new to Wear OS
Thanks!
Not sure how this can work and suspect this is where the NullPointerException comes from
private var _binding: FragmentOverviewBinding? = null
private val binding get() = _binding!!
compare to the example at https://developer.android.com/topic/libraries/view-binding#fragments
which sets _binding in onCreateView
My problem is when I try to change the fragment with the bottom navigation it works except in one case. I have 5 fragments, when I enter in the 3rd and after go to the 5th, the icon of the 5th donĀ“t change it color. After that when I do another navigation the APP crash and give me the next error:
java.lang.IndexOutOfBoundsException: fromIndex = -1
at java.util.ArrayList.subListRangeCheck(ArrayList.java:1014)
at java.util.ArrayList.subList(ArrayList.java:1008)
at androidx.navigation.fragment.FragmentNavigator.popBackStack(FragmentNavigator.kt:80)
at androidx.navigation.NavController.popBackStackInternal(NavController.kt:275)
at androidx.navigation.NavController.popBackStackInternal(NavController.kt:558)
at androidx.navigation.NavController.navigate(NavController.kt:1682)
at androidx.navigation.NavController.navigate(NavController.kt:1541)
at androidx.navigation.NavController.navigate(NavController.kt:1468)
at androidx.navigation.ui.NavigationUI.onNavDestinationSelected(NavigationUI.kt:92)
at androidx.navigation.ui.NavigationUI.setupWithNavController$lambda-6(NavigationUI.kt:602)
at androidx.navigation.ui.NavigationUI.$r8$lambda$6wzEv9QqEZKdQFS1sQQy-bdQvgE(Unknown Source:0)
at androidx.navigation.ui.NavigationUI$$ExternalSyntheticLambda2.onNavigationItemSelected(Unknown Source:2)
at com.google.android.material.navigation.NavigationBarView$1.onMenuItemSelected(NavigationBarView.java:295)
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.navigation.NavigationBarMenuView$1.onClick(NavigationBarMenuView.java:133)
at android.view.View.performClick(View.java:7520)
at android.view.View.performClickInternal(View.java:7489)
at android.view.View.access$3600(View.java:826)
at android.view.View$PerformClick.run(View.java:28555)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:233)
at android.app.ActivityThread.main(ActivityThread.java:8010)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:631)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:978)
But if previously i clicked in the 5th fragment and go to the 3rd and after 5th again it works correctly.
For my code I follow this tutorial and the code are the same in both cases.
edit: Here is my code to add the navigation in MainActivity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
if(savedInstanceState == null){
setupBottomNavigationBar()
}
private fun setupBottomNavigationBar(){
val graphs = setOf(
R.id.firstFragment,
R.id.secondFragment,
R.id.thirdFragment,
R.id.fourthFragment,
R.id.fithFragment
)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_container) as NavHostFragment
currentNavController = navHostFragment.navController
val bottomNavigation = findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNavigation.setupWithNavController(currentNavController)
appBarConfiguration = AppBarConfiguration(graphs)
}
activity_main.xml
<LinearLayout 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:orientation="vertical"
tools:context=".activities.MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="#navigation/super_nav"
/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="#menu/bottom_nav"
app:itemIconTint="#drawable/botton_navigation_colors"
app:labelVisibilityMode="unlabeled"
app:itemIconSize="35dp"
/>
bottom_nav.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/first_navigation"
android:icon="#drawable/ic_first_black"
android:contentDescription="#string/first_desc"
android:title="#string/first_title" />
<item
android:id="#+id/second_navigation"
android:icon="#drawable/ic_second"
android:contentDescription="#string/second_desc"
android:title="#string/second_title" />
<item
android:id="#+id/third_navigation"
android:icon="#drawable/ic_third"
android:contentDescription="#string/third_desc"
android:title="#string/third_title" />
<item
android:id="#+id/fourth_navigation"
android:icon="#drawable/ic_fourth"
android:contentDescription="#string/fourth_desc"
android:title="#string/fourth_title" />
<item
android:id="#+id/fifth_navigation"
android:icon="#drawable/ic_fifth"
android:contentDescription="#string/fifth_desc"
android:title="#string/fifth_title" />
And super_nav.xml
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/super_nav"
app:startDestination="#+id/main_navigation">
<include app:graph="#navigation/first_navigation"/>
<include app:graph="#navigation/second_navigation"/>
<include app:graph="#navigation/third_navigation" />
<include app:graph="#navigation/fourth_cart_navigation" />
<include app:graph="#navigation/fifth_navigation" />
I've just fixed very similar issue. The problem in my case was that I had destinations with the same id in several navigation subgraphs.
I mean:
<include app:graph="#navigation/first_sub_graph"/>
<include app:graph="#navigation/second_sub_graph"/>
<navigation
android:id="#+id/first_sub_graph"
..>
...
<fragment
android:id="#+id/**destinationID_1**"
...
/>
</navigation>
<navigation
android:id="#+id/second_sub_graph"
..>
...
<fragment
android:id="#+id/**destinationID_1**"
...
/>
</navigation>
I am migrating an Android app from Kotlin synthetic to Jetpack ViewBinding. App is having
a Bottom Navigation View with Navigation graph setup. But when I try to navigate from the underlying fragment using findNavController().navigate method, App crashes and Logcat shows the following crash logs. This started happening after I migrated Main Activity to View Binding.
2022-02-21 15:34:44.626 29145-29145/com.x.7 E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.x.y, PID: 29145
java.lang.IllegalStateException: no current navigation node
at androidx.navigation.NavController.navigate(NavController.java:897)
at androidx.navigation.NavController.navigate(NavController.java:877)
at androidx.navigation.NavController.navigate(NavController.java:863)
---------
---------
---------
---------
at android.view.View.performClick(View.java:7125)
at android.view.View.performClickInternal(View.java:7102)
at android.view.View.access$3500(View.java:801)
at android.view.View$PerformClick.run(View.java:27340)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7397)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:935)
My current setup of the app is as follow:
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=".ui.main.MainActivity">
<fragment
android:id="#+id/fragmentContainerView"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1"
app:defaultNavHost="true"
app:layout_constraintBottom_toTopOf="#+id/bottomNavigationView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:layout_width="match_parent"
android:layout_height="#dimen/_1sdp"
android:background="#color/divider_color"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="#id/bottomNavigationView"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottomNavigationView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/screen_background"
app:itemIconSize="#dimen/icon_bottom_navigation_bar"
app:labelVisibilityMode="unlabeled"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_behavior="com.google.android.material.behavior.HideBottomViewOnScrollBehavior"
app:menu="#menu/bottom_nav_menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.kt
private fun setupBottomNavigationBar() {
val navGraphIds = listOf(
R.navigation.nav_graph_tab_first,
R.navigation.nav_graph_tab_second,
R.navigation.nav_graph_tab_third
)
// Setup the bottom navigation view with a list of navigation graphs
val controller = bottomNavigationView.setupWithNavController(
navGraphIds = navGraphIds,
containerId = R.id.fragmentContainerView,
fragmentManager = supportFragmentManager,
intent = intent,
onNavigationItemSelectionChanged = this::handleNavigationItemSelectionChanges
)
currentNavController = controller
currentNavController?.observe(this, { renderNavControllerChanges(it) })
}
FirstTabFragment.kt
class FirstTabFragment<VB : ViewBinding> : ViewBindingBaseFragment<VB>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.clickMe.setOnClickListener {
findNavController().navigate(R.id.action_detail_fragment)
}
}
}
Here when the user taps on the Click Me button, App gets crashed with the error java.lang.IllegalStateException: no current navigation node.
Seem I'm stuck here and can't remove the kotlin-extension plugin due to this crash.
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 trying to use the Navigation Architecture Component (NavHostFragment) with a Navigation Drawer (widget.NavigationView). I get one of the following two errors.
1) This can happen when selecting an item from the drawer several times:
java.lang.IllegalArgumentException: navigation destination app.myDomain.navdrawertrials:id/action_rootFragment_to_settingsFragment is unknown to this NavController
2) This happens from my real code base that is set up the same way as the simplified sample below AFAICT. Why would a current navigation node not be set?
java.lang.IllegalStateException: no current navigation node
Simplified Code
MainActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
setupToolbar()
setupNavDrawer()
setupNavigation()
}
private fun setupToolbar() {
setSupportActionBar( toolbar )
}
private fun setupNavigation() {
val navController = findNavController( R.id.nav_host_fragment)
setupActionBarWithNavController( navController, main_activity_drawer_layout )
}
private fun setupNavDrawer() {
val toggle = ActionBarDrawerToggle(
this,
main_activity_drawer_layout,
toolbar,
R.string.navigation_drawer_open,
R.string.navigation_drawer_close)
main_activity_drawer_layout.addDrawerListener(toggle)
toggle.syncState()
nav_drawer.setNavigationItemSelectedListener {
val navController = findNavController( R.id.nav_host_fragment )
when (it.itemId) {
R.id.nav_drawer_root_menu_item -> navController.navigate(R.id.rootFragment)
R.id.nav_drawer_first_menu_item -> navController.navigate(R.id.action_rootFragment_to_firstFragment)
R.id.nav_drawer_settings_menu_item -> navController.navigate(R.id.action_rootFragment_to_settingsFragment)
}
main_activity_drawer_layout.closeDrawer(GravityCompat.START)
true
}
}
override fun onSupportNavigateUp() = findNavController(R.id.nav_host_fragment).navigateUp()
}
main_activity.xml
<android.support.v4.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/main_activity_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
layout="#layout/main_activity_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.NavigationView
android:id="#+id/nav_drawer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_drawer_header"
app:menu="#menu/nav_drawer_menu" />
</android.support.v4.widget.DrawerLayout>
nav_drawer_menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/nav_drawer_root_menu_item"
android:title="To Root" />
<item
android:id="#+id/nav_drawer_first_menu_item"
android:title="To First" />
<item
android:id="#+id/nav_drawer_settings_menu_item"
android:title="To Settings" />
</menu>
main_activity_content.xml
<android.support.design.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=".MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary" />
</android.support.design.widget.AppBarLayout>
<fragment
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navGraph="#navigation/nav_graph"
app:defaultNavHost="true"
/>
<android.support.design.widget.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
app:srcCompat="#android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
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/rootFragment">
<fragment
android:id="#+id/rootFragment"
android:name="app.anytune.navdrawertrials.RootFragment"
android:label="root_fragment"
tools:layout="#layout/root_fragment" >
<action
android:id="#+id/action_rootFragment_to_firstFragment"
app:destination="#id/firstFragment" />
<action
android:id="#+id/action_rootFragment_to_settingsFragment"
app:destination="#id/settingsFragment" />
</fragment>
<fragment
android:id="#+id/firstFragment"
android:name="app.anytune.navdrawertrials.FirstFragment"
android:label="first_fragment"
tools:layout="#layout/first_fragment" />
<fragment
android:id="#+id/settingsFragment"
android:name="app.anytune.navdrawertrials.SettingsFragment"
android:label="settings_fragment"
tools:layout="#layout/settings_fragment" />
</navigation>
root_fragment.xml (other nodes are similar empty fragments with just a label)
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".RootFragment">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Root Fragment" />
</FrameLayout>
Regarding the first error, based on your code, when user select 'first' or 'settings' from drawer he is transferred to 'first' or 'settings' fragment using action_rootFragment_to_firstFragment or action_rootFragment_to_settingsFragment action, but if you try to select again 'first' or 'settings' from drawer there is no action_rootFragment_to_firstFragment or action_rootFragment_to_settingsFragment action inside firstFragment or settingsFragment element inside navigation graph.
The solution is to change:
when (it.itemId) {
R.id.nav_drawer_root_menu_item -> navController.navigate(R.id.rootFragment)
R.id.nav_drawer_first_menu_item -> navController.navigate(R.id.action_rootFragment_to_firstFragment)
R.id.nav_drawer_settings_menu_item -> navController.navigate(R.id.action_rootFragment_to_settingsFragment)
}
to:
when (it.itemId) {
R.id.nav_drawer_root_menu_item -> navController.navigate(R.id.rootFragment)
R.id.nav_drawer_first_menu_item -> navController.navigate(R.id.firstFragment)
R.id.nav_drawer_settings_menu_item -> navController.navigate(R.id.settingsFragment)
}
The better solution is to tie destinations to menu-driven UI components(in your case drawer), change your menu items id to same as destinations id's, like this:
<item
android:id="#+id/rootFragment"
android:title="To Root" />
<item
android:id="#+id/firstFragment"
android:title="To First" />
<item
android:id="#+id/settingsFragment"
android:title="To Settings" />
and add
setupWithNavController(nav_view, navController )
inside your main activity, instead of
nav_drawer.setNavigationItemSelectedListener {
val navController = findNavController( R.id.nav_host_fragment )
when (it.itemId) {
R.id.nav_drawer_root_menu_item -> navController.navigate(R.id.rootFragment)
R.id.nav_drawer_first_menu_item -> navController.navigate(R.id.action_rootFragment_to_firstFragment)
R.id.nav_drawer_settings_menu_item -> navController.navigate(R.id.action_rootFragment_to_settingsFragment)
}
main_activity_drawer_layout.closeDrawer(GravityCompat.START)
true
}
2) If the crash occurs on orientation change. Use the below on the activity
upgrade navigation version module to 2.2.0 or above
implementation "androidx.navigation:navigation-fragment-ktx:2.2.0"
implementation "androidx.navigation:navigation-ui-ktx:2.2.0"