How to make ActionView use the color of the MenuItem

Viewed 30

I have added a TextView as an ActionView to the menu items of my NavigationView. I use this TextView to show how many unread elements there are. I followed this article for the implementation: https://medium.com/android-news/android-adding-badge-or-count-to-the-navigation-drawer-84c93af1f4d9
Now I want to make sure the text color of this ActionView always matches the tint of the MenuItem it is attached to.

I have currently implemented it like this:

        navController.addOnDestinationChangedListener { _, destination, _ ->
            updateBadgesTextColor(destination)
        }

// some other code

    private fun updateBadgesTextColor(currentDestination: NavDestination) {
        val menu = binding.mainNavView.menu
        val currentMenuGraph = getMenuParentGraph(currentDestination, menu)
        
        val defaultColor = ContextCompat.getColor(this, R.color.drawer_layout_default_tint_color)
        val selectedColor = ContextCompat.getColor(this, R.color.drawer_layout_selected_tint_color)
        
        menu.forEach { menuItem ->
            (menuItem.actionView as? TextView)?.setTextColor(
                if (menuItem.itemId == currentMenuGraph?.id) {
                    selectedColor
                } else {
                    defaultColor
                }
            )
        }
    }

    private fun getMenuParentGraph(destination: NavDestination, menu: Menu): NavGraph? {
        var currentGraph: NavGraph? = destination.parent
        
        while (currentGraph != null && menu.findItem(currentGraph.id) == null) {
            /*
            note: In my app the menu item and the navGraph that are linked always have the same id.
            In an app where this isn't the case, this function won't work correctly.
            */
            currentGraph = currentGraph.parent
        }
        
        return currentGraph
    }

While this does do what I want it to do, it isn't at all efficient because it iterates through the entire list of MenuItems every single time the NavDestination changes, even when the NavDestination change doesn't result in a change of selected MenuItem.
Is there a more efficient way to solve this?

1 Answers

You can track the previous selected destination Id and reset it for a new call to addOnDestinationChangedListener callback.

Normally in navigation components, the destination ids should be the same as thoses correspond to the navView menu Ids.

var previousId = -1 

navController.addOnDestinationChangedListener { _, destination, _ ->

        if (previousId != -1) {
            val previous = binding.mainNavView.menu.findItem(previousId).actionView as TextView?
            previous?.setTextColor(ResourcesCompat.getColor(resources, R.color.drawer_layout_default_tint_color, null))
        }       

        val current = binding.mainNavView.menu.findItem(destination.id).actionView as TextView?
        current.setTextColor(ResourcesCompat.getColor(resources, R.color.drawer_layout_selected_tint_color, null))
}

Make sure to handle saving the previousId permanently to avoid losing it in configuration changes.

Related