使用导航抽屉或工具栏菜单动画化片段过渡

Animate fragment transitions with using Navigation drawer or Toolbar menu

我有一个导航抽屉和工具栏菜单,我可以从中转到另一个片段。我用它来导航:

override fun onNavigationItemSelected(item: MenuItem): Boolean
{
    drawerLayout.close()
    return NavigationUI.onNavDestinationSelected(
        item,
        findNavController(R.id.navHostFragment)
    )
}

如何在切换片段时添加自定义动画?


Answer your own question。我遇到过这个问题,但找不到任何对我来说 100% 有效的答案,所以也许这会对某些人有所帮助。

重写的方法 onNavigationItemSelected 应该是这样的:

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
    }
}
  1. 此方法可防止转到当前选定的片段。因此,当用户在 Fragment A 中并在导航抽屉中再次选择 Fragment A 时,不会发生任何事情,导航抽屉将隐藏。

  2. 此方法还可以防止“循环”片段,堆栈中可能只有一个片段是从导航抽屉或工具栏菜单中选择的。例如。在导航抽屉中是 Fragment AFragment B。用户在 Fragment Home,然后转到 Fragment A 并在 Fragment B 旁边。如果用户单击后退按钮,应用程序将返回到 Fragment Home,而不是 Fragment A。此外,可以使用 R.id.homeFragment 而不是在 while 循环中查找 startDestination。如果有人想要允许“循环”片段,只需删除代码中的这一部分。

我已经测试了这个解决方案,它似乎运行良好,但当然也有可能无法正常工作。


如果有人想要带有淡入淡出 in/fade 和垂直平移的简单动画:

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>