2

This is an extended question from How to prevent view double click , but for bottom navigation. Especially one set up with setupWithNavController().

The usual way of using your own click listener and compare previous click time doesn't work since Android NavigationUI is now handling the click.

For example I have a BottomNavigationView set up in fragment:

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val navHostFragment = childFragmentManager.findFragmentById(R.id.content_nav_host) as NavHostFragment
        val navController = navHostFragment.findNavController()
        val bottomNavigationView = view.findViewById<BottomNavigationView>(R.id.bottom_navigation)
        bottomNavigationView.setupWithNavController(navController)
    }

If swapping between the fragment too fast, I get this error:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.project, PID: 11136
    java.lang.IllegalArgumentException: No destination with ID 2131296272 is on the NavController's back stack. The current destination is Destination(com.example.project/page1) label=page1 class=com.example.project.features.main.page1.Page1Fragment
        at androidx.navigation.NavController.getBackStackEntry(NavController.java:1358)

enter image description here

Exception is from Fragment is still in the middle of onViewCreated(), some navigation happened and changed the navController backstrack before the fragment viewModel is access for the first time.

I guess the easiest way is to prevent bottom navigation from being able to click too fast from the start.

Demo and stacktrace

https://github.com/yatw/QuickSwapBottomNav

BabyishTank
  • 1,329
  • 3
  • 18
  • 39
  • 1
    Can you use this callback to determine whether you should bubble up the command to the NavController? https://developer.android.com/reference/androidx/navigation/NavController#addOnDestinationChangedListener(androidx.navigation.NavController.OnDestinationChangedListener) Otherwise I'd think about disabling the UI component that initiates the navigation when navigation has begun. – Luke Duncan Nov 20 '21 at 03:36
  • So where do you actually access the `submissionViewModel`? Please include the entire stack trace and your code where you access the ViewModel. – ianhanniballake Nov 20 '21 at 05:29
  • @ianhanniballake I created a minimal project to reproduce the problem, please checkout the link – BabyishTank Nov 20 '21 at 18:54

1 Answers1

2

Found out backstack entry is updated on navigation success, and due to rapid tapping on different destination, somehow the destination id requested is already no longer in backstack entry.

Agreed that putting throttle may help preventing these, but I think need to use manual approach, using setOnItemSelectedListener().

But anyway, tried to reproduce using project attached in the question.
And, it is can no longer reproduced using manual approach without throttling on my side

bottomNavigationView.setOnItemSelectedListener { menuItem ->
            when (menuItem.itemId) {
                R.id.fragment1NestGraph -> {
                    val navOptions = NavOptions.Builder()
                        .setPopUpTo(R.id.fragment1NestGraph, true)
                        .build()
                    navController.navigate(R.id.fragment1NestGraph, Bundle(), navOptions)
                }
                R.id.fragment2NestGraph -> {
                    val navOptions = NavOptions.Builder()
                        .setPopUpTo(R.id.fragment2NestGraph, true)
                        .build()
                    navController.navigate(R.id.fragment2NestGraph, Bundle(), navOptions)
                }
                ...etc
            }

            true
        }
Putra Nugraha
  • 574
  • 1
  • 4
  • 12
  • This won't help at all, since calling `navigate` is exactly what the default behavior does. – ianhanniballake Nov 20 '21 at 05:28
  • thanks, actually I tried this too, wrapped into my own listener class and compare previous click time, but the problem is clicking system back button won't automatically update the bottom nav icon. – BabyishTank Nov 20 '21 at 17:13
  • This would be my last approach to update bottom nav icon myself if I cannot use the given `setupWithNavController()` – BabyishTank Nov 20 '21 at 17:15
  • Unfortunately, it was the drawback @BabyishTank, `setupWithNavController()` is handling the checked item logic as well. So, we need to manually handle these as well using `addOnDestinationChangedListener()` for back pressed case – Putra Nugraha Nov 22 '21 at 03:38
  • Anyway, there's an update for the answer, thought I miss some concept in the previous answer – Putra Nugraha Nov 22 '21 at 03:57