不幸的是,基于NavigationUtils的解决方案,仅使用NavGraph类的findNode()方法,有一个严重的缺点 - 不可能导航到指向当前NavGraph本身的目标。换句话说,findNode()方法将找不到任何内容。
下面的图表中可以考虑作为示例操作action_startingFragment_to_startingFragment:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/some_graph"
app:startDestination="@id/startingFragment">
<fragment
android:id="@+id/startingFragment"
android:name="com.xxx.StartingFragment">
<action
android:id="@+id/action_startingFragment_to_startingFragment"
app:destination="@id/some_graph"
app:popUpTo="@id/startingFragment"
app:popUpToInclusive="true" />
</fragment>
</navigation>
为考虑到提到的情况,还需要检查找到的当前节点本身是否为目标终点。
因此,扩展函数将如下所示:
fun NavController.navigateSafe(@IdRes actionId: Int, args: Bundle?) {
currentDestination?.let { currentDestination ->
val navAction = currentDestination.getAction(actionId)
if (navAction != null) {
val destinationId = navAction.destinationId
if (destinationId != 0) {
val currentNode = currentDestination as? NavGraph ?: currentDestination.parent
if (currentNode?.id == destinationId || <--------- THIS CONDITION IS THE KEY
currentNode?.findNode(destinationId) != null
) {
navigate(actionId, args, null)
}
}
}
}
}
更新:
还有一个更有趣的情况缺失,就是当目标指向父级NavGraph本身时,如下所示。
Parent graph:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_graph"
app:startDestination="@id/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="com.xxx.main.MainFragment">
<action
android:id="@+id/action_main_to_some"
app:destination="@id/some_graph"/>
</fragment>
<include app:graph="@navigation/some_graph" />
</navigation>
Child graph:
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/some_graph"
app:startDestination="@id/someFragment">
<fragment
android:id="@+id/someFragment"
android:name="com.xxx.some.SomeFragment">
<action
android:id="@+id/action_some_to_main"
app:destination="@id/main_graph"
app:popUpTo="@id/someFragment"
app:popUpToInclusive="true" />
</fragment>
</navigation>
Action action_some_to_main is target one.
Therefore, extension function need to be modified:
fun NavController.navigateSafe(@IdRes actionId: Int, args: Bundle?) {
currentDestination?.let { currentDestination ->
val navAction = currentDestination.getAction(actionId)
if (navAction != null) {
val destinationId = navAction.destinationId
if (destinationId != 0) {
val currentNode = currentDestination as? NavGraph ?: currentDestination.parent
if (currentNode?.findDestination(destinationId) != null) { <----- CHANGED HERE
navigate(actionId, args, null)
}
}
}
}
}
private fun NavGraph.findDestination(destinationId: Int): NavDestination? {
if (id == destinationId) return this
val node = findNode(destinationId)
if (node != null) return node
return parent?.findDestination(destinationId)
}
navController?.navigateUp()
可能会解决你的问题。 - Asad Mahmood