导航组件如何通过编程设置转场动画

21
昨天我遇到了一个问题,需要在我的baseFragment中从nav_graph.xml设置动画,并以编程方式获取包括enterAnim和exitAnim资源的当前节点的操作对象。在这里找不到解决方案,因此我们开始吧。 首先,我们需要在res文件夹中提供anim文件夹来放置我们的动画,因为它很饥饿。 slide_in_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:duration="250"
        android:fromXDelta="-100%"
        android:fromYDelta="0%"
        android:toXDelta="0%"
        android:toYDelta="0%" />
</set>

你可以在github或stackoverflow上轻松找到其他动画。

这是我的nav_graph.xml片段,我们将从中进行转换。

<fragment
    android:id="@+id/kebabsFragment"
    android:name="com.kebabkrabby.kebabapp.KebabFragment"
    android:label="so many kebabs"
    tools:layout="@layout/fragment_kebab">

    <action
        android:id="@+id/action_kebabs_to_kebab_detail"
        app:destination="@id/kebabDetailFragment"
        app:enterAnim="@anim/slide_in_right"
        app:exitAnim="@anim/slide_out_left" />
</fragment>

现在在KebabFragment.tk中,您将调用baseFragment方法进行转换以进入所需kebab的详细信息,然后从回退栈中弹出

//navigateAndClean(actionId, cleanFragmentId)
navigateAndClean(R.id.action_kebabs_to_kebab_detail, R.id.kebabsFragment)

在我们的baseFragment.kt文件中

internal fun navigateAndClean(actionId: Int, currentFragmentIdToClear: Int) {

    val navBuilder = NavOptions.Builder()

    val navController = Navigation.findNavController(getRootView())
    val currNavigationNode = navController.graph.findNode(currentFragmentIdToClear) // NavDestination of kebabsFragment object in nav_graph.xml
    val action = currNavigationNode?.getAction(actionId) // finally we get this action_kebabs_to_kebab_detail action object

    action?.navOptions?.enterAnim?.let {  //do we have animation or not?
        navBuilder.setEnterAnim(it)
    }

    action?.navOptions?.exitAnim?.let {
        navBuilder.setExitAnim(it)
    }

    navBuilder.setPopUpTo(currentFragmentIdToClear, true)       //remove from backstack

    navController.navigate(actionId, null, navBuilder.build())
}

有些人会问: "嘿,Kebab先生,我该如何获得getRootView()?",而Kebab先生则会看着你说:“伙计,看看这个世界,我们一起可以取得很大的成就。”

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    rootView = inflater.inflate(mContentLayoutResourceId, container, false)
    return rootView
}

fun getRootView(): View {
    return rootView
}

小麦肉串。享受美食。


KebabFragment 继承自 baseFragment 吗?在我看来,该操作已经嵌入了转换,为什么需要重新应用它们? - Stachu
是的,KebabFragment继承自BaseFragment,而BaseFragment则拥有navigateAndClean函数,该函数读取nav_graph.xml中的片段设置,并在存在转换时应用它们。如果存在转换,则将其放入navBuilder中。 - Kebab Krabby
所以你的意思是,当传递给 navigateAndClean 的操作具有转换时,它们不会自动应用,需要手动应用,对吗? - Stachu
navController.navigate() 不接受 action 对象,而是接受 actionId,这是一个很大的区别。它不知道任何转换。您必须创建带有动画的自定义 navBuilder 对象。因此,首先从图表中获取 actionId 的 action,然后检查是否存在任何转换,如果存在,则将其设置为自定义 navBuilder 对象。 - Kebab Krabby
好的,明白了。为什么不使用 navController.navigate(actionId, null, action?.navOptions) 呢?它不能解决问题吗? - Stachu
1个回答

4

导航组件提供了入场和出场过渡的内置动画选项,下面附有参考示例代码块。

在Java/Kotlin中添加动画的代码块如下:

navigate(
navController, resId, bundle,
NavOptions.Builder()
    .setPopUpTo(R.id.splashFragment, true)
    .setEnterAnim(R.anim.fade_in)
    .setExitAnim(R.anim.fade_out)
    .setPopEnterAnim(R.anim.fade_in)
    .setPopExitAnim(R.anim.fade_out)
    .build()
)

在 XML 文件中添加动画的代码块

<fragment
android:id="@+id/kebabsFragment"
android:name="com.kebabkrabby.kebabapp.KebabFragment"
android:label="so many kebabs"
tools:layout="@layout/fragment_kebab">
<action
    android:id="@+id/confirmationAction"
    app:destination="@id/confirmationFragment"
    app:enterAnim="@anim/slide_in_right"
    app:exitAnim="@anim/slide_out_left"
    app:popEnterAnim="@anim/slide_in_left"
    app:popExitAnim="@anim/slide_out_right" />
</fragment>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接