I have a Navigation drawer and Toolbar menu where I can go to another fragment. I used this to navigate:
override fun onNavigationItemSelected(item: MenuItem): Boolean
{
drawerLayout.close()
return NavigationUI.onNavDestinationSelected(
item,
findNavController(R.id.navHostFragment)
)
}
How I can add custom animation while switching fragments?
It is Answer your own question
. I have faced this problem and couldn't find any answer which would work 100% well in my case so maybe this will help somebody.
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>