如何以编程方式更改导航图的起始目标[Jetpack]

Shy*_*ine 30 android android-navigation android-jetpack android-architecture-navigation

基本上,我有以下导航图:

在此输入图像描述

我希望fragment 2在到达之后将导航图中的起点更改为正确(为了防止fragment 1在按下后退按钮时返回 - 就像启动画面一样).

这是我的代码:

navGraph = navController.getGraph();
navGraph.setStartDestination(R.id.fragment2);
navController.setGraph(navGraph);
Run Code Online (Sandbox Code Playgroud)

但是,显然它不起作用,fragment 1按下后退按钮后又回来了.

我做错了吗?还有其他解决方案吗?

Ale*_*nko 35

更新:

如果您有这样的导航图:

<fragment
    android:id="@+id/firstFragment"
    android:name="com.appname.package.FirstFragment" >
    <action
        android:id="@+id/action_firstFragment_to_secondFragment"
        app:destination="@id/secondFragment" /> 
</fragment>

<fragment
    android:id="@+id/secondFragment"
    android:name="com.appname.package.SecondFragment"/>
Run Code Online (Sandbox Code Playgroud)

并且您希望导航到第二个片段并将其作为图形的根,指定下一个NavOptions:

NavOptions navOptions = new NavOptions.Builder()
        .setPopUpTo(R.id.firstFragment, true)
        .build();
Run Code Online (Sandbox Code Playgroud)

并使用它们进行导航:

Navigation.findNavController(view).navigate(R.id.action_firstFragment_to_secondFragment, bundle, navOptions);
Run Code Online (Sandbox Code Playgroud)

setPopUpTo(int destinationId, boolean inclusive) - 在导航之前弹出到指定目的地.这将从后堆栈中弹出所有不匹配的目标,直到找到此目标.

destinationId - 弹出目的地,清除所有干预目的地.

inclusive - 如果从后栈中弹出给定目标,则为true.


老答案

不推荐使用:已弃用操作的clearTask属性和关联的API NavOptions.

资料来源:https://developer.android.com/jetpack/docs/release-notes


如果您想要将根片段更改为fragment 2(例如,按下后退按钮,fragment 2您将退出应用程序),您应该将下一个属性添加到您的action或destination:

<fragment
    android:id="@+id/firstFragment"
    android:name="com.appname.package.FirstFragment" >
<action
    android:id="@+id/action_firstFragment_to_secondFragment"
    app:destination="@id/secondFragment"
    app:popUpTo="@+id/firstFragment"
    app:popUpToInclusive="true" /> 
</fragment>

<fragment
    android:id="@+id/secondFragment"
    android:name="com.appname.package.SecondFragment"/>
Run Code Online (Sandbox Code Playgroud)

实际上它看起来是另一种方式:

findNavController(fragment).navigate(
    FirstFragmentDirections.actionFirstFragmentToSecondFragment())
Run Code Online (Sandbox Code Playgroud)

我加入app:clearTask="true"了行动.


现在当您执行导航fragment 1时fragment 2使用下一个代码:

app:clearTask="true"
Run Code Online (Sandbox Code Playgroud)

  • 自alpha02以来,不推荐使用clearTask标志和setClearTask,请查看这些/sf/ask/3536033091/, /sf/ask/3523527871/ (2认同)

sza*_*ske 16

我为此找到了解决方案,但它很难看。我想这是一个 alpha 库可以预期的,但我希望谷歌考虑简化/修复这个问题,因为这是一个非常流行的导航模式。

Alexey 的解决方案对我不起作用。我的问题是我的操作栏上显示了向上箭头,方法是使用:

NavigationUI.setupActionBarWithNavController(this, navController)

如果我按照上面 Alexey 的建议去做,我的新开始片段仍然有一个箭头指向我的初始开始片段。如果我按下向上箭头,我的应用程序将重新启动,过渡到自身(新的开始片段)

这是获得我想要的代码所需的代码:

  • Fragment #1 是我的应用程序最初启动的地方
  • 我可以在片段 #1 中进行身份验证检查,然后以编程方式将开始更改为片段 #2。
  • 进入片段 #2 后,没有向上箭头,按后退按钮不会将您带到片段 #1。

这是完成此操作的代码。在我的活动的 onCreate 中:

// Setup the toolbar
val toolbar = findViewById<Toolbar>(R.id.toolbar)
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(false)

// Configure the navigation
val navHost = nav_host_fragment as NavHostFragment
graph = navHost.navController
        .navInflater.inflate(R.navigation.nav_graph)
graph.startDestination = R.id.welcomeFragment

// This seems to be a magical command. Not sure why it's needed :(
navHost.navController.graph = graph

NavigationUI.setupActionBarWithNavController(this, navHost.navController)
Run Code Online (Sandbox Code Playgroud)

并且:

fun makeHomeStart(){
    graph.startDestination = R.id.homeFragment
}
Run Code Online (Sandbox Code Playgroud)

然后在片段 #1 的 onActivityCreated 中,根据 Alexey 的建议:

override fun onActivityCreated(savedInstanceState: Bundle?) {
    ...

    // Check for user authentication
    if(sharedViewModel.isUserAuthenticated()) {

        (activity as MainActivity).makeHomeStart() //<---- THIS is the key
        val navOptions = NavOptions.Builder()
                .setPopUpTo(R.id.welcomeFragment, true)
                .build()
        navController.navigate(R.id.action_welcomeFragment_to_homeFragment,null,navOptions)

    } else {
        navController.navigate(R.id.action_welcomeFragment_to_loginFragment)
    }
}
Run Code Online (Sandbox Code Playgroud)

关键代码是: (activity as MainActivity).makeHomeStart()它只是在活动中运行一个方法来改变图表 startDestination。我可以清理它并将其变成一个界面,但我会等待谷歌并希望他们改进整个过程。方法“setPopUpTo”对我来说似乎命名不佳,并且命名从图形中切出的片段并不直观。他们在 navOptions 中进行这些更改对我来说也很奇怪。我认为 navOptions 只会与它们连接的导航操作有关。

我什至不知道是什么navHost.navController.graph = graph,但没有它向上箭头返回。:(

我正在使用导航 1.0.0-alpha06。


Doc*_*ger 13

您也可以尝试以下方法。

val navController = findNavController(R.id.nav_host_fragment)
if (condition) {
    navController.setGraph(R.navigation.nav_graph_first)
} else {
    navController.setGraph(R.navigation.nav_graph_second)
}
Run Code Online (Sandbox Code Playgroud)

与其尝试弹出开始目的地或手动导航到目标目的地,不如拥有另一个具有不同工作流程的导航图。当您有条件地想要完全不同的导航流程时,这会更好。


Din*_*nny 9

在 MainActivity.kt

    val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val inflater = navHostFragment.navController.navInflater
    val graph = inflater.inflate(R.navigation.booking_navigation)

    if (isTrue){
        graph.startDestination = R.id.DetailsFragment
    }else {
        graph.startDestination = R.id.OtherDetailsFragment
    }

    val navController = navHostFragment.navController
    navController.setGraph(graph, intent.extras)
Run Code Online (Sandbox Code Playgroud)

从 nav_graph.xml 中删除 startDestination

?xml version="1.0" encoding="utf-8"?>
<!-- app:startDestination="@id/oneFragment" -->

<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/navigation_main">

  <fragment
    android:id="@+id/DetailFragment"
    android:name="DetailFragment"
    android:label="fragment_detail"
    tools:layout="@layout/fragment_detail"/>
  <fragment
    android:id="@+id/OtherDetailFragment"
    android:name="OtherDetailFragment"
    android:label="fragment_other_detail"
    tools:layout="@layout/fragment_other_detail"/>
</navigation>
Run Code Online (Sandbox Code Playgroud)

  • 您必须删除活动 XML 文件中的图表标签“app:navGraph” (12认同)

Pab*_*des 5

您实际上并不需要弹出 Splash Fragment。它可以在您的应用程序生命周期的剩余时间内保留在那里。您应该做的是从启动屏幕确定下一个要显示的屏幕。

登录状态机流程

在上图中,您可以在闪屏状态下询问是否有保存的LoginToken。如果为空,则导航到登录屏幕。

登录屏幕完成后,您可以分析结果,保存令牌并导航到下一个片段主屏幕。

当在主屏幕中按下后退按钮时,您将向启动屏幕发送回一条结果消息,指示它完成应用程序。

下面的代码可能有帮助:

val nextDestination = if (loginSuccess) {
    R.id.action_Dashboard
} else {
    R.id.action_NotAuthorized
}

val options = NavOptions.Builder()
    .setPopUpTo(R.id.loginParentFragment, true)
    .build()

findNavController().navigate(nextDestination, null, options)
Run Code Online (Sandbox Code Playgroud)