如何为导航操作定义默认动画?
How do I define default animations for Navigation Actions?
我正在使用 Android Studio 3.2 Canary 14 和 The Navigation Architecture Component。有了它,您可以像使用 Intents 一样定义过渡动画。
但是动画被设置为导航图中动作的属性,如下所示:
<fragment
android:id="@+id/startScreenFragment"
android:name="com.example.startScreen.StartScreenFragment"
android:label="fragment_start_screen"
tools:layout="@layout/fragment_start_screen" >
<action
android:id="@+id/action_startScreenFragment_to_findAddressFragment"
app:destination="@id/findAddressFragment"
app:enterAnim="@animator/slide_in_right"
app:exitAnim="@animator/slide_out_left"
app:popEnterAnim="@animator/slide_in_left"
app:popExitAnim="@animator/slide_out_right"/>
</fragment>
为图表中的所有操作定义这变得乏味!
有没有办法在动作上定义一组默认动画?
我没有为此使用样式。
R.anim 定义了默认动画(最终):
nav_default_enter_anim
nav_default_exit_anim
nav_default_pop_enter_anim
nav_default_pop_exit_anim
要更改此行为,您必须使用自定义 NavOptions、
因为这是那些动画被分配给 NavAction.
的地方
可以用 NavOptions.Builder:
分配这些
protected NavOptions getNavOptions() {
NavOptions navOptions = new NavOptions.Builder()
.setEnterAnim(R.anim.default_enter_anim)
.setExitAnim(R.anim.default_exit_anim)
.setPopEnterAnim(R.anim.default_pop_enter_anim)
.setPopExitAnim(R.anim.default_pop_exit_anim)
.build();
return navOptions;
}
很可能需要创建一个 DefaultNavFragment
,扩展 class androidx.navigation.fragment(那里的文档似乎还没有完成)。
因此您可以像这样将这些 NavOptions
传递给 NavHostFragment
:
NavHostFragment.findNavController(this).navigate(R.id.your_action_id, null, getNavOptions());
或者...查看该包的 attrs.xml
时;这些动画 风格:
<resources>
<declare-styleable name="NavAction">
<attr name="enterAnim" format="reference"/>
<attr name="exitAnim" format="reference"/>
<attr name="popEnterAnim" format="reference"/>
<attr name="popExitAnim" format="reference"/>
...
</declare-styleable>
</resources>
这意味着,可以定义相应的样式 - 并将这些定义为主题的一部分...
可以在 styles.xml
:
中定义它们
<style name="Theme.Default" parent="Theme.AppCompat.Light.NoActionBar">
<!-- these should be the correct ones -->
<item name="NavAction_enterAnim">@anim/default_enter_anim</item>
<item name="NavAction_exitAnim">@anim/default_exit_anim</item>
<item name="NavAction_popEnterAnim">@anim/default_pop_enter_anim</item>
<item name="NavAction_popExitAnim">@anim/default_pop_exit_anim</item>
</style>
也可以在res/anim
中定义默认动画:
res/anim/nav_default_enter_anim.xml
res/anim/nav_default_exit_anim.xml
res/anim/nav_default_pop_enter_anim.xml
res/anim/nav_default_pop_exit_anim.xml
如上所述,R.anim 定义了默认动画:
nav_default_enter_anim
nav_default_exit_anim
nav_default_pop_enter_anim
nav_default_pop_exit_anim
但您可以轻松覆盖它们。
只需在您的应用程序模块中创建您自己的四个同名动画资源(澄清一下,其中一个的 id 是 your.package.name.R.anim.nav_default_enter_anim
)并编写您想要的动画。
可以使用自定义 androidx.navigation.fragment.Navigator
。
我将演示如何覆盖 fragment
导航。这是我们的自定义导航器。注意setAnimations()
方法
@Navigator.Name("fragment")
class MyAwesomeFragmentNavigator(
private val context: Context,
private val manager: FragmentManager, // Should pass childFragmentManager.
private val containerId: Int
): FragmentNavigator(context, manager, containerId) {
private val backStack by lazy {
this::class.java.superclass!!.getDeclaredField("mBackStack").let {
it.isAccessible = true
it.get(this) as ArrayDeque<Integer>
}
}
override fun navigate(destination: Destination, args: Bundle?, navOptions: NavOptions?, navigatorExtras: Navigator.Extras?): NavDestination? {
if (manager.isStateSaved) {
logi("Ignoring navigate() call: FragmentManager has already"
+ " saved its state")
return null
}
var className = destination.className
if (className[0] == '.') {
className = context.packageName + className
}
val frag = instantiateFragment(context, manager,
className, args)
frag.arguments = args
val ft = manager.beginTransaction()
navOptions?.let { setAnimations(it, ft) }
ft.replace(containerId, frag)
ft.setPrimaryNavigationFragment(frag)
@IdRes val destId = destination.id
val initialNavigation = backStack.isEmpty()
// TODO Build first class singleTop behavior for fragments
val isSingleTopReplacement = (navOptions != null && !initialNavigation
&& navOptions.shouldLaunchSingleTop()
&& backStack.peekLast()?.toInt() == destId)
val isAdded: Boolean
isAdded = if (initialNavigation) {
true
} else if (isSingleTopReplacement) { // Single Top means we only want one
instance on the back stack
if (backStack.size > 1) { // If the Fragment to be replaced is on the FragmentManager's
// back stack, a simple replace() isn't enough so we
// remove it from the back stack and put our replacement
// on the back stack in its place
manager.popBackStack(
generateBackStackName(backStack.size, backStack.peekLast()!!.toInt()),
FragmentManager.POP_BACK_STACK_INCLUSIVE)
ft.addToBackStack(generateBackStackName(backStack.size, destId))
}
false
} else {
ft.addToBackStack(generateBackStackName(backStack.size + 1, destId))
true
}
if (navigatorExtras is Extras) {
for ((key, value) in navigatorExtras.sharedElements) {
ft.addSharedElement(key!!, value!!)
}
}
ft.setReorderingAllowed(true)
ft.commit()
// The commit succeeded, update our view of the world
return if (isAdded) {
backStack.add(Integer(destId))
destination
} else {
null
}
}
private fun setAnimations(navOptions: NavOptions, transaction: FragmentTransaction) {
transaction.setCustomAnimations(
navOptions.enterAnim.takeIf { it != -1 } ?: android.R.anim.fade_in,
navOptions.exitAnim.takeIf { it != -1 } ?: android.R.anim.fade_out,
navOptions.popEnterAnim.takeIf { it != -1 } ?: android.R.anim.fade_in,
navOptions.popExitAnim.takeIf { it != -1 } ?: android.R.anim.fade_out
)
}
private fun generateBackStackName(backStackIndex: Int, destId: Int): String? {
return "$backStackIndex-$destId"
}
}
下一步我们必须将导航器添加到 NavController
。这是一个如何设置它的例子:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragmentContainer)!!
with (findNavController(R.id.fragmentContainer)) {
navigatorProvider += MyAwesomeFragmentNavigator(this@BaseContainerActivity, navHostFragment.childFragmentManager, R.id.fragmentContainer)
setGraph(navGraphId)
}
}
xml 没什么特别的:)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment
android:id="@+id/fragmentContainer"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true" />
</LinearLayout>
现在图中的每个片段都会有 alpha 转换
我找到了需要扩展 NavHostFragment
的解决方案。它类似于 但较少涉及代码。大多数情况下,它需要从标准更改所有 xml defaultNavHost 片段名称:
<fragment
app:defaultNavHost="true"
...
android:name="androidx.navigation.fragment.NavHostFragment"
至:
<fragment
app:defaultNavHost="true"
...
android:name="your.app.package.fragments.NavHostFragmentWithDefaultAnimations"
NavHostFragmentWithDefaultAnimations
的代码:
package your.app.package.fragments
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.FragmentManager
import androidx.navigation.*
import androidx.navigation.fragment.FragmentNavigator
import androidx.navigation.fragment.NavHostFragment
import your.app.package.R
// Those are navigation-ui (androidx.navigation.ui) defaults
// used in NavigationUI for NavigationView and BottomNavigationView.
// Set yours here
private val defaultNavOptions = navOptions {
anim {
enter = R.animator.nav_default_enter_anim
exit = R.animator.nav_default_exit_anim
popEnter = R.animator.nav_default_pop_enter_anim
popExit = R.animator.nav_default_pop_exit_anim
}
}
private val emptyNavOptions = navOptions {}
class NavHostFragmentWithDefaultAnimations : NavHostFragment() {
override fun onCreateNavController(navController: NavController) {
super.onCreateNavController(navController)
navController.navigatorProvider.addNavigator(
// this replaces FragmentNavigator
FragmentNavigatorWithDefaultAnimations(requireContext(), childFragmentManager, id)
)
}
}
/**
* Needs to replace FragmentNavigator and replacing is done with name in annotation.
* Navigation method will use defaults for fragments transitions animations.
*/
@Navigator.Name("fragment")
class FragmentNavigatorWithDefaultAnimations(
context: Context,
manager: FragmentManager,
containerId: Int
) : FragmentNavigator(context, manager, containerId) {
override fun navigate(
destination: Destination,
args: Bundle?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
): NavDestination? {
// this will try to fill in empty animations with defaults when no shared element transitions are set
// https://developer.android.com/guide/navigation/navigation-animate-transitions#shared-element
val shouldUseTransitionsInstead = navigatorExtras != null
val navOptions = if (shouldUseTransitionsInstead) navOptions
else navOptions.fillEmptyAnimationsWithDefaults()
return super.navigate(destination, args, navOptions, navigatorExtras)
}
private fun NavOptions?.fillEmptyAnimationsWithDefaults(): NavOptions =
this?.copyNavOptionsWithDefaultAnimations() ?: defaultNavOptions
private fun NavOptions.copyNavOptionsWithDefaultAnimations(): NavOptions =
let { originalNavOptions ->
navOptions {
launchSingleTop = originalNavOptions.shouldLaunchSingleTop()
popUpTo(originalNavOptions.popUpTo) {
inclusive = originalNavOptions.isPopUpToInclusive
}
anim {
enter =
if (originalNavOptions.enterAnim == emptyNavOptions.enterAnim) defaultNavOptions.enterAnim
else originalNavOptions.enterAnim
exit =
if (originalNavOptions.exitAnim == emptyNavOptions.exitAnim) defaultNavOptions.exitAnim
else originalNavOptions.exitAnim
popEnter =
if (originalNavOptions.popEnterAnim == emptyNavOptions.popEnterAnim) defaultNavOptions.popEnterAnim
else originalNavOptions.popEnterAnim
popExit =
if (originalNavOptions.popExitAnim == emptyNavOptions.popExitAnim) defaultNavOptions.popExitAnim
else originalNavOptions.popExitAnim
}
}
}
}
您可以在导航图中更改动画 xml 或通过传递 navOptions 在代码中更改动画。
要禁用默认动画,请传递动画值为 0 的 navOptions 或传递 navigatorExtras(设置共享转换)。
测试版本:
implementation "androidx.navigation:navigation-fragment-ktx:2.3.1"
implementation "androidx.navigation:navigation-ui-ktx:2.3.1"
这是我的解决方案,它在我的应用程序中运行良好。
public void navigate(int resId, Bundle bundle) {
NavController navController = getNavController();
if (navController == null) return;
NavDestination currentNode;
NavBackStackEntry currentEntry = navController.getCurrentBackStackEntry();
if (currentEntry == null) currentNode = navController.getGraph();
else currentNode = currentEntry.getDestination();
final NavAction navAction = currentNode.getAction(resId);
final NavOptions navOptions;
if (navAction == null || navAction.getNavOptions() == null) navOptions = ExampleUtil.defaultNavOptions;
else if (navAction.getNavOptions().getEnterAnim() == -1
&& navAction.getNavOptions().getPopEnterAnim() == -1
&& navAction.getNavOptions().getExitAnim() == -1
&& navAction.getNavOptions().getPopExitAnim() == -1) {
navOptions = new NavOptions.Builder()
.setLaunchSingleTop(navAction.getNavOptions().shouldLaunchSingleTop())
.setPopUpTo(resId, navAction.getNavOptions().isPopUpToInclusive())
.setEnterAnim(ExampleUtil.defaultNavOptions.getEnterAnim())
.setExitAnim(ExampleUtil.defaultNavOptions.getExitAnim())
.setPopEnterAnim(ExampleUtil.defaultNavOptions.getPopEnterAnim())
.setPopExitAnim(ExampleUtil.defaultNavOptions.getPopExitAnim())
.build();
} else navOptions = navAction.getNavOptions();
navController.navigate(resId, bundle, navOptions);
}
我已经创建了扩展程序并调用了它,而不是在需要时调用 navigation
。
fun NavController.navigateWithDefaultAnimation(directions: NavDirections) {
navigate(directions, navOptions {
anim {
enter = R.anim.anim_fragment_enter_transition
exit = R.anim.anim_fragment_exit_transition
popEnter = R.anim.anim_fragment_pop_enter_transition
popExit = R.anim.anim_fragment_pop_exit_transition
}
})
}
findNavController().navigateWithDefaultAnimation(HomeFragmentDirections.homeToProfile())
我正在使用 Android Studio 3.2 Canary 14 和 The Navigation Architecture Component。有了它,您可以像使用 Intents 一样定义过渡动画。
但是动画被设置为导航图中动作的属性,如下所示:
<fragment
android:id="@+id/startScreenFragment"
android:name="com.example.startScreen.StartScreenFragment"
android:label="fragment_start_screen"
tools:layout="@layout/fragment_start_screen" >
<action
android:id="@+id/action_startScreenFragment_to_findAddressFragment"
app:destination="@id/findAddressFragment"
app:enterAnim="@animator/slide_in_right"
app:exitAnim="@animator/slide_out_left"
app:popEnterAnim="@animator/slide_in_left"
app:popExitAnim="@animator/slide_out_right"/>
</fragment>
为图表中的所有操作定义这变得乏味!
有没有办法在动作上定义一组默认动画?
我没有为此使用样式。
R.anim 定义了默认动画(最终):
nav_default_enter_anim
nav_default_exit_anim
nav_default_pop_enter_anim
nav_default_pop_exit_anim
要更改此行为,您必须使用自定义 NavOptions、
因为这是那些动画被分配给 NavAction.
的地方可以用 NavOptions.Builder:
分配这些protected NavOptions getNavOptions() {
NavOptions navOptions = new NavOptions.Builder()
.setEnterAnim(R.anim.default_enter_anim)
.setExitAnim(R.anim.default_exit_anim)
.setPopEnterAnim(R.anim.default_pop_enter_anim)
.setPopExitAnim(R.anim.default_pop_exit_anim)
.build();
return navOptions;
}
很可能需要创建一个 DefaultNavFragment
,扩展 class androidx.navigation.fragment(那里的文档似乎还没有完成)。
因此您可以像这样将这些 NavOptions
传递给 NavHostFragment
:
NavHostFragment.findNavController(this).navigate(R.id.your_action_id, null, getNavOptions());
或者...查看该包的 attrs.xml
时;这些动画 风格:
<resources>
<declare-styleable name="NavAction">
<attr name="enterAnim" format="reference"/>
<attr name="exitAnim" format="reference"/>
<attr name="popEnterAnim" format="reference"/>
<attr name="popExitAnim" format="reference"/>
...
</declare-styleable>
</resources>
这意味着,可以定义相应的样式 - 并将这些定义为主题的一部分...
可以在 styles.xml
:
<style name="Theme.Default" parent="Theme.AppCompat.Light.NoActionBar">
<!-- these should be the correct ones -->
<item name="NavAction_enterAnim">@anim/default_enter_anim</item>
<item name="NavAction_exitAnim">@anim/default_exit_anim</item>
<item name="NavAction_popEnterAnim">@anim/default_pop_enter_anim</item>
<item name="NavAction_popExitAnim">@anim/default_pop_exit_anim</item>
</style>
也可以在res/anim
中定义默认动画:
res/anim/nav_default_enter_anim.xml
res/anim/nav_default_exit_anim.xml
res/anim/nav_default_pop_enter_anim.xml
res/anim/nav_default_pop_exit_anim.xml
如上所述,R.anim 定义了默认动画:
nav_default_enter_anim
nav_default_exit_anim
nav_default_pop_enter_anim
nav_default_pop_exit_anim
但您可以轻松覆盖它们。
只需在您的应用程序模块中创建您自己的四个同名动画资源(澄清一下,其中一个的 id 是 your.package.name.R.anim.nav_default_enter_anim
)并编写您想要的动画。
可以使用自定义 androidx.navigation.fragment.Navigator
。
我将演示如何覆盖 fragment
导航。这是我们的自定义导航器。注意setAnimations()
方法
@Navigator.Name("fragment")
class MyAwesomeFragmentNavigator(
private val context: Context,
private val manager: FragmentManager, // Should pass childFragmentManager.
private val containerId: Int
): FragmentNavigator(context, manager, containerId) {
private val backStack by lazy {
this::class.java.superclass!!.getDeclaredField("mBackStack").let {
it.isAccessible = true
it.get(this) as ArrayDeque<Integer>
}
}
override fun navigate(destination: Destination, args: Bundle?, navOptions: NavOptions?, navigatorExtras: Navigator.Extras?): NavDestination? {
if (manager.isStateSaved) {
logi("Ignoring navigate() call: FragmentManager has already"
+ " saved its state")
return null
}
var className = destination.className
if (className[0] == '.') {
className = context.packageName + className
}
val frag = instantiateFragment(context, manager,
className, args)
frag.arguments = args
val ft = manager.beginTransaction()
navOptions?.let { setAnimations(it, ft) }
ft.replace(containerId, frag)
ft.setPrimaryNavigationFragment(frag)
@IdRes val destId = destination.id
val initialNavigation = backStack.isEmpty()
// TODO Build first class singleTop behavior for fragments
val isSingleTopReplacement = (navOptions != null && !initialNavigation
&& navOptions.shouldLaunchSingleTop()
&& backStack.peekLast()?.toInt() == destId)
val isAdded: Boolean
isAdded = if (initialNavigation) {
true
} else if (isSingleTopReplacement) { // Single Top means we only want one
instance on the back stack
if (backStack.size > 1) { // If the Fragment to be replaced is on the FragmentManager's
// back stack, a simple replace() isn't enough so we
// remove it from the back stack and put our replacement
// on the back stack in its place
manager.popBackStack(
generateBackStackName(backStack.size, backStack.peekLast()!!.toInt()),
FragmentManager.POP_BACK_STACK_INCLUSIVE)
ft.addToBackStack(generateBackStackName(backStack.size, destId))
}
false
} else {
ft.addToBackStack(generateBackStackName(backStack.size + 1, destId))
true
}
if (navigatorExtras is Extras) {
for ((key, value) in navigatorExtras.sharedElements) {
ft.addSharedElement(key!!, value!!)
}
}
ft.setReorderingAllowed(true)
ft.commit()
// The commit succeeded, update our view of the world
return if (isAdded) {
backStack.add(Integer(destId))
destination
} else {
null
}
}
private fun setAnimations(navOptions: NavOptions, transaction: FragmentTransaction) {
transaction.setCustomAnimations(
navOptions.enterAnim.takeIf { it != -1 } ?: android.R.anim.fade_in,
navOptions.exitAnim.takeIf { it != -1 } ?: android.R.anim.fade_out,
navOptions.popEnterAnim.takeIf { it != -1 } ?: android.R.anim.fade_in,
navOptions.popExitAnim.takeIf { it != -1 } ?: android.R.anim.fade_out
)
}
private fun generateBackStackName(backStackIndex: Int, destId: Int): String? {
return "$backStackIndex-$destId"
}
}
下一步我们必须将导航器添加到 NavController
。这是一个如何设置它的例子:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragmentContainer)!!
with (findNavController(R.id.fragmentContainer)) {
navigatorProvider += MyAwesomeFragmentNavigator(this@BaseContainerActivity, navHostFragment.childFragmentManager, R.id.fragmentContainer)
setGraph(navGraphId)
}
}
xml 没什么特别的:)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment
android:id="@+id/fragmentContainer"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true" />
</LinearLayout>
现在图中的每个片段都会有 alpha 转换
我找到了需要扩展 NavHostFragment
的解决方案。它类似于
<fragment
app:defaultNavHost="true"
...
android:name="androidx.navigation.fragment.NavHostFragment"
至:
<fragment
app:defaultNavHost="true"
...
android:name="your.app.package.fragments.NavHostFragmentWithDefaultAnimations"
NavHostFragmentWithDefaultAnimations
的代码:
package your.app.package.fragments
import android.content.Context
import android.os.Bundle
import androidx.fragment.app.FragmentManager
import androidx.navigation.*
import androidx.navigation.fragment.FragmentNavigator
import androidx.navigation.fragment.NavHostFragment
import your.app.package.R
// Those are navigation-ui (androidx.navigation.ui) defaults
// used in NavigationUI for NavigationView and BottomNavigationView.
// Set yours here
private val defaultNavOptions = navOptions {
anim {
enter = R.animator.nav_default_enter_anim
exit = R.animator.nav_default_exit_anim
popEnter = R.animator.nav_default_pop_enter_anim
popExit = R.animator.nav_default_pop_exit_anim
}
}
private val emptyNavOptions = navOptions {}
class NavHostFragmentWithDefaultAnimations : NavHostFragment() {
override fun onCreateNavController(navController: NavController) {
super.onCreateNavController(navController)
navController.navigatorProvider.addNavigator(
// this replaces FragmentNavigator
FragmentNavigatorWithDefaultAnimations(requireContext(), childFragmentManager, id)
)
}
}
/**
* Needs to replace FragmentNavigator and replacing is done with name in annotation.
* Navigation method will use defaults for fragments transitions animations.
*/
@Navigator.Name("fragment")
class FragmentNavigatorWithDefaultAnimations(
context: Context,
manager: FragmentManager,
containerId: Int
) : FragmentNavigator(context, manager, containerId) {
override fun navigate(
destination: Destination,
args: Bundle?,
navOptions: NavOptions?,
navigatorExtras: Navigator.Extras?
): NavDestination? {
// this will try to fill in empty animations with defaults when no shared element transitions are set
// https://developer.android.com/guide/navigation/navigation-animate-transitions#shared-element
val shouldUseTransitionsInstead = navigatorExtras != null
val navOptions = if (shouldUseTransitionsInstead) navOptions
else navOptions.fillEmptyAnimationsWithDefaults()
return super.navigate(destination, args, navOptions, navigatorExtras)
}
private fun NavOptions?.fillEmptyAnimationsWithDefaults(): NavOptions =
this?.copyNavOptionsWithDefaultAnimations() ?: defaultNavOptions
private fun NavOptions.copyNavOptionsWithDefaultAnimations(): NavOptions =
let { originalNavOptions ->
navOptions {
launchSingleTop = originalNavOptions.shouldLaunchSingleTop()
popUpTo(originalNavOptions.popUpTo) {
inclusive = originalNavOptions.isPopUpToInclusive
}
anim {
enter =
if (originalNavOptions.enterAnim == emptyNavOptions.enterAnim) defaultNavOptions.enterAnim
else originalNavOptions.enterAnim
exit =
if (originalNavOptions.exitAnim == emptyNavOptions.exitAnim) defaultNavOptions.exitAnim
else originalNavOptions.exitAnim
popEnter =
if (originalNavOptions.popEnterAnim == emptyNavOptions.popEnterAnim) defaultNavOptions.popEnterAnim
else originalNavOptions.popEnterAnim
popExit =
if (originalNavOptions.popExitAnim == emptyNavOptions.popExitAnim) defaultNavOptions.popExitAnim
else originalNavOptions.popExitAnim
}
}
}
}
您可以在导航图中更改动画 xml 或通过传递 navOptions 在代码中更改动画。 要禁用默认动画,请传递动画值为 0 的 navOptions 或传递 navigatorExtras(设置共享转换)。
测试版本:
implementation "androidx.navigation:navigation-fragment-ktx:2.3.1"
implementation "androidx.navigation:navigation-ui-ktx:2.3.1"
这是我的解决方案,它在我的应用程序中运行良好。
public void navigate(int resId, Bundle bundle) {
NavController navController = getNavController();
if (navController == null) return;
NavDestination currentNode;
NavBackStackEntry currentEntry = navController.getCurrentBackStackEntry();
if (currentEntry == null) currentNode = navController.getGraph();
else currentNode = currentEntry.getDestination();
final NavAction navAction = currentNode.getAction(resId);
final NavOptions navOptions;
if (navAction == null || navAction.getNavOptions() == null) navOptions = ExampleUtil.defaultNavOptions;
else if (navAction.getNavOptions().getEnterAnim() == -1
&& navAction.getNavOptions().getPopEnterAnim() == -1
&& navAction.getNavOptions().getExitAnim() == -1
&& navAction.getNavOptions().getPopExitAnim() == -1) {
navOptions = new NavOptions.Builder()
.setLaunchSingleTop(navAction.getNavOptions().shouldLaunchSingleTop())
.setPopUpTo(resId, navAction.getNavOptions().isPopUpToInclusive())
.setEnterAnim(ExampleUtil.defaultNavOptions.getEnterAnim())
.setExitAnim(ExampleUtil.defaultNavOptions.getExitAnim())
.setPopEnterAnim(ExampleUtil.defaultNavOptions.getPopEnterAnim())
.setPopExitAnim(ExampleUtil.defaultNavOptions.getPopExitAnim())
.build();
} else navOptions = navAction.getNavOptions();
navController.navigate(resId, bundle, navOptions);
}
我已经创建了扩展程序并调用了它,而不是在需要时调用 navigation
。
fun NavController.navigateWithDefaultAnimation(directions: NavDirections) {
navigate(directions, navOptions {
anim {
enter = R.anim.anim_fragment_enter_transition
exit = R.anim.anim_fragment_exit_transition
popEnter = R.anim.anim_fragment_pop_enter_transition
popExit = R.anim.anim_fragment_pop_exit_transition
}
})
}
findNavController().navigateWithDefaultAnimation(HomeFragmentDirections.homeToProfile())