Prevent fast click in BottomNavigationView

Viewed 643

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

1 Answers

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