This is how the overridden method onNavigationItemSelected should looks:
override fun onNavigationItemSelected(item: MenuItem): Boolean
{
drawerLayout.close()
// this part checks if current fragment is the same as destination
return if (findNavController(R.id.navHostFragment).currentDestination?.id != item.itemId)
{
val builder = NavOptions.Builder()
.setLaunchSingleTop(true)
.setEnterAnim(R.anim.enter_left_to_right)
.setExitAnim(R.anim.exit_right_to_left)
.setPopEnterAnim(R.anim.popenter_right_to_left)
.setPopExitAnim(R.anim.popexit_left_to_right)
// this part set proper pop up destination to prevent "looping" fragments
if (item.order and Menu.CATEGORY_SECONDARY == 0)
{
var startDestination: NavDestination? =
findNavController(R.id.navHostFragment).graph
while (startDestination is NavGraph)
{
val parent = startDestination
startDestination = parent.findNode(parent.startDestination)
}
builder.setPopUpTo(
startDestination!!.id,
false
)
}
val options = builder.build()
return try
{
findNavController(R.id.navHostFragment).navigate(item.itemId, null, options)
true
}
catch (e: IllegalArgumentException) // couldn't find destination, do nothing
{
false
}
}
else
{
false
}
}
This method prevents going to current, selected fragments. So when a user is in the Fragment A and in the Navigation drawer again selects Fragment A nothing will happen, Navigation drawer will just hide.
This method also prevents "looping" fragments, there could be only one fragment on the stack which was selected from the Navigation drawer or Toolbar menu. E.g. in the navigation drawer is Fragment A and Fragment B. The user is in Fragment Home then go to Fragment A and next to Fragment B. If a user clicks back button app will go back to Fragment Home, not to the Fragment A. Also, instead of finding startDestination in a while loop, it is possible to use R.id.homeFragment. If anyone wants to allow "looping" fragments just delete this part in code.
I have tested this solution and it seems to work well but of course there can be something which doesn't work exactly as it should.
If someone wants simple animation with fade in/fade out and vertical translation:
R.anim.enter_left_to_right
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@integer/fragment_anim_time"
android:fromXDelta="-100%"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="0%" />
<alpha
android:duration="@integer/fragment_anim_time"
android:fromAlpha="0.5"
android:toAlpha="1" />
</set>
R.anim.exit_right_to_left
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@integer/fragment_anim_time"
android:fromXDelta="0%"
android:fromYDelta="0%"
android:toXDelta="100%"
android:toYDelta="0%" />
<alpha
android:duration="@integer/fragment_anim_time"
android:fromAlpha="1"
android:toAlpha="0.5" />
</set>
R.anim.popenter_right_to_left
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@integer/fragment_anim_time"
android:fromXDelta="100%"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="0%" />
<alpha
android:duration="@integer/fragment_anim_time"
android:fromAlpha="0.5"
android:toAlpha="1" />
</set>
R.anim.popexit_left_to_right
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@integer/fragment_anim_time"
android:fromXDelta="0%"
android:fromYDelta="0%"
android:toXDelta="-100%"
android:toYDelta="0%" />
<alpha
android:duration="@integer/fragment_anim_time"
android:fromAlpha="1"
android:toAlpha="0.5" />
</set>
res/values/integers.xml
<integer name="fragment_anim_time">250</integer>