gary0707
gary0707

Reputation: 313

NavController.OnDestinationChangedListener passes a destination.id not matching the triggered Navigation Action ID

In my Android project I have a very simple Navigation Graph, including two fragments: Master and Detail:

<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"
   app:startDestination="@id/wordsListFragment">

    <fragment
        android:id="@+id/wordsListFragment"
        android:name="com.***.presentation.view.WordsListFragment"
        android:label="List"
        tools:layout="@layout/words_list_fragment">
        <action
            android:id="@+id/action_wordsListFragment_to_wordDetailsFragment"
            app:destination="@id/wordDetailsFragment" />
    </fragment>
    <fragment
        android:id="@+id/wordDetailsFragment"
        android:name="com.***.presentation.view.WordDetailsFragment"
        android:label="Details"
        tools:layout="@layout/word_details_fragment" />
</navigation>

The navigation itself works perfectly fine in both directions including the "Back" behaviour. In that project I have a single activity where I implement OnDestinationChangedListener. All this according to the following documentation from Google: NavController Updating UI

I call the following method when the user clicks on a list item (while being on the master fragment):

findNavController().navigate(R.id.action_wordsListFragment_to_wordDetailsFragment, null)

Then in the parent activity I have the following implementation:

private fun setupNavController() {
    navigationController = findNavController(R.id.nav_mainhost_fragment_container)
    navigationController.addOnDestinationChangedListener(mainDestinationChangedListener)
    appBarConfiguration = AppBarConfiguration(navigationController.graph)
    setupActionBarWithNavController(navigationController, appBarConfiguration)
}

and that is the listener object:

private val mainDestinationChangedListener = 
NavController.OnDestinationChangedListener { controller, destination, arguments ->        

if (destination.id == R.id.action_wordsListFragment_to_wordDetailsFragment) {
        actionBar?.hide()
    } else {
        actionBar?.show()
    }
}

but the destination.id does not match the R.id.action_wordsListFragment_to_wordDetailsFragment

I have tried to clean up the project, clean up the IDE cache, the gradle cache, but the generated identifiers still does not match. I have also tried to use Navigation via Safe Args:

val action = WordsListFragmentDirections.actionWordsListFragmentToWordDetailsFragment()
findNavController().navigate(action)

but the results in the given listener are always the same (i.e. not matching).

Some values from debugging:

findNavController().navigate(1000021) //R.id.action_wordsListFragment_to_wordDetailsFragment

but the next call on stack has another value: enter image description here what also matches the destination.id values passed to the OnDestinationChangedListener:

destination.id //2131231018

Any hints from your side are more than welcome. I just want to recognise the destination or the action ID and adjust the ToolBar accordingly.

Upvotes: 22

Views: 15554

Answers (4)

akaMahesh
akaMahesh

Reputation: 381

I was also facing this, for me the solution was something like this

com.dainikbhaskar.features.videofeed.R.id.video_feed_dest -> 2131297511 where as R.id.video_feed_dest -> 1000296

i have multimodule project structure.. check we first one.. with full package import not from app module..

Upvotes: -1

Nanzbz
Nanzbz

Reputation: 209

The issue is with the Android studio debugger. If I print the value in logcat or assign it to a variable, then the value is 2XXXXXXXXX but if I evaluate with debugger then the value is 1XXXXXX.

I have checked the action id(R.id.xxxx) hex code in the APK. When I convert the hex code into integer, it gives a value in 2XXXXXXXXX which is equal to the value that got printed in the logcat

Upvotes: 6

ilatyphi95
ilatyphi95

Reputation: 605

The above solution does not work, Instead of matching on the destination fragment id, you can match on the fully qualified class name of the destination fragment, your code becomes

navController.addOnDestinationChangedListener{ nc: NavController, nd: NavDestination, args: Bundle? ->

            val destinationClassName = (nc.currentDestination as FragmentNavigator.Destination).className

            when(destinationClassName) {

                "com.*domainName.*PackageName.*className" -> {
                    "Do you thing here"
                }

                else -> {
                    "Cater for the else block here"
                }
            }
        }

Upvotes: -1

Mohammed Alaa
Mohammed Alaa

Reputation: 3320

You are comparing fragmentId with actionId so it's always false

here if (destination.id == R.id.action_wordsListFragment_to_wordDetailsFragment)

As destination.id is fragmentId and R.id.action_wordsListFragment_to_wordDetailsFragment is actionId

To make it work you should compare two fragment ids like

if (destination.id == R.id.wordDetailsFragment)

*Edit

First you should find you navControler , then listen to its destination change.

val navController = findNavController(this,R.id.nav_host_fragment)// this maybe change
navController.addOnDestinationChangedListener { controller, destination, arguments ->
   if(destination.id == R.id.wordDetailsFragment) {
       actionBar?.hide()
   } else {
       actionBar?.show()
   }
}

Upvotes: 18

Related Questions