22

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.

gary0707
  • 313
  • 1
  • 3
  • 10
  • Did you find any solution? I am also getting different ids? I am not sure how to compare – musooff Jun 04 '20 at 06:22
  • Unfortunately not. – gary0707 Jun 04 '20 at 21:02
  • 4
    I did some testing. That's kind of strange but it works even tho the numbers are different. During debugging if you check numbers they maybe 1000021 and 2131231018, but equality check proceeds. I am guessing, even though int values are different as they are annotated with @ResId they might have different equality checks during runtime. – musooff Jun 05 '20 at 08:42
  • @musooff that's not actually true, I check for equality and it still gives me false and I can't do what I'd like to – FabioR Nov 25 '20 at 22:02
  • 1
    I believe the destination id in this action would be R.id.wordDetailsFragment. Try comparing destination.id with destination fragment's id. – lokesh Jan 08 '21 at 21:37

4 Answers4

17

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()
   }
}
matthias_h
  • 11,356
  • 9
  • 22
  • 40
Mohammed Alaa
  • 3,140
  • 2
  • 19
  • 21
  • 6
    I am afraid you are NOT right. I have checked such scenario already, but it's not the case. The *destination.id* is 2131231021, while the *R.id.wordDetailsFragment* is: 1000241 It's not my first time with Android development. I just don't understand what (and how) changes the *destination.id* – gary0707 Mar 14 '20 at 15:17
  • @gary0707 I have updated the answer hope it will help you. – Mohammed Alaa Mar 14 '20 at 15:38
  • 3
    Thank you for your efforts, but you still don't get the problem I am describing above. My listener called: `mainDestinationChangedListener` is already added, working and triggered when necessary. The mechanism of notifying the listener works, but the `destination.id` does not match either the Navigation Action ID or the destination (details) fragment. – gary0707 Mar 15 '20 at 16:11
  • @gary0707 are you setting `mainDestinationChangedListener` to the navControler like `navController.addOnDestinationChangedListener(mainDestinationChangedListener)` – Mohammed Alaa Mar 15 '20 at 18:20
  • Yes, otherwise how all the mechanism would work, triggering the listener callback? FYI: I have updated my question with code I use so you will hopefully trust me in that regard ;-) – gary0707 Mar 16 '20 at 07:06
6

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

Nanzbz
  • 199
  • 2
  • 5
-1

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"
                }
            }
        }
ilatyphi95
  • 555
  • 5
  • 22
  • It looks like a workaround not a proper usage of the Android API. Type casting, usage of hardcoded fully qualified names - really? I don't think so. FYI: the code I have posted in my question works on another project so it must be sth. with particular project configuration/setup. – gary0707 Jul 28 '20 at 05:35
  • have you tried my suggestion, I had the same problem and after lots of searches without success, I decided to look at all the possibilities provided by the arguments provided and I found I could use a workaround that works all the time. Sometimes you need a work - around especially if there is no solution in view. And the fully qualified class name is hard coded in order to no to make the solution as simple as possible – ilatyphi95 Jul 28 '20 at 09:31
  • No, I have not tried it. In a production code I don't use workaround like the one you have proposed. As I mentioned, I had that problem in one of the project, while on the other one the Navigation Component works fine. I believe that we should use any API per design, instead of hacking it. Edsker Dijkstra recommends to "...avoid clever tricks like the plague". – gary0707 Jul 28 '20 at 19:26
-1

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..

akaMahesh
  • 383
  • 2
  • 9