当你有这样一个导航图:
<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"/>
如果您想导航到第二个片段并将其作为图形的根,请指定以下NavOptions
:
NavOptions navOptions = new NavOptions.Builder()
.setPopUpTo(R.id.firstFragment, true)
.build();
并将它们用于导航:
Navigation.findNavController(view).navigate(R.id.action_firstFragment_to_secondFragment, bundle, navOptions);
setPopUpTo(int destinationId, boolean inclusive)
- 通过弹出给定目标之前的所有非匹配目标来导航。这将从返回堆栈中弹出所有不匹配的目标,直到找到此目标。
destinationId
- 要弹出并清除所有中间目标的目标。
inclusive
- true表示从返回堆栈中也要弹出给定的目标。
<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"/>
然后在您的代码中:
findNavController(fragment).navigate(
FirstFragmentDirections.actionFirstFragmentToSecondFragment())
已弃用: clearTask
属性及其在 NavOptions
中的相关 API 已弃用。
来源:https://developer.android.com/jetpack/docs/release-notes
如果您想将根片段更改为 fragment 2
(例如在按下 fragment 2
上的后退按钮后退出应用程序),您应该将下一个属性放入您的 action
或 destination
中:
app:clearTask="true"
实际上,它看起来是这样的:
<fragment
android:id="@+id/firstFragment"
android:name="com.appname.package.FirstFragment"
android:label="fragment_first" >
<action
android:id="@+id/action_firstFragment_to_secondFragment"
app:destination="@id/secondFragment"
app:clearTask="true" />
</fragment>
<fragment
android:id="@+id/secondFragment"
android:name="com.appname.package.SecondFragment"
android:label="fragment_second"/>
我在操作中加入了 app:clearTask="true"
。
现在,当您从 片段 1
导航到 片段 2
时,请使用以下代码:
Navigation.findNavController(view)
.navigate(R.id.action_firstFragment_to_secondFragment);
在 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)
从 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>
我找到了解决方案,但它很丑陋。我想这是可以预料的,因为这是一个alpha库,但我希望Google能够简化/修复这个问题,因为这是一个相当流行的导航模式。
Alexey的解决方案对我没有用。我的问题是我在使用以下代码在Actionbar上显示向上箭头:
NavigationUI.setupActionBarWithNavController(this, navController)
如果我按照Alexey上面建议的做法去做,我的新起始片段仍然指向我的初始起始片段。如果我按那个向上箭头,我的应用程序会有点重新启动,转换到它本身(新起始片段)
以下是代码,可以实现我想要的效果:
以下是实现此操作的代码。在我的Activity的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)
还有:
fun makeHomeStart(){
graph.startDestination = R.id.homeFragment
}
然后在片段#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)
}
}
关键代码为:(activity as MainActivity).makeHomeStart()
,它只是在活动中运行一个方法,该方法更改了图形的起始位置。我可以将其清理并转换为接口,但我会等待Google并希望他们改进整个过程。对我来说,“setPopUpTo”方法的命名似乎很糟糕,而且不直观,因为您正在命名从图形中剪切的片段。对我来说,他们在navOptions中进行这些更改也很奇怪。我认为navOptions只与它们连接的导航操作有关。
我甚至不知道navHost.navController.graph = graph
是做什么的,但没有它,向上箭头就会返回。:(
我正在使用Navigation 1.0.0-alpha06。
您也可以尝试以下方法。
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)
}
与其尝试弹出起始目标或手动导航到目标目的地,最好使用具有不同工作流程的另一个导航图。
当您需要有条件地完全不同的导航流程时,这将更加优秀。
您实际上不需要关闭闪屏片段,它可以一直存在于您的应用程序生命周期中。您应该在闪屏屏幕上确定下一个要显示的屏幕。
在上图中,您可以在Splash Screen状态中询问是否有保存的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)
如果您有一个与此类似的导航XML文件:
<?xml version="1.0" encoding="utf-8"?>
<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/mobile_navigation"
app:startDestination="@+id/nav_home">
<fragment
android:id="@+id/nav_home"
android:name="HomeFragment"
android:label="@string/menu_home"
tools:layout="@layout/fragment_home" />
<fragment
android:id="@+id/nav_users"
android:name="UsersFragment"
android:label="@string/users"
tools:layout="@layout/fragment_users" />
<fragment
android:id="@+id/nav_settings"
android:name="SettingsFragment"
android:label="@string/settings"
tools:layout="@layout/fragment_settings" />
</navigation>
yourElement.setOnClickListener {
view.findNavController().navigate(R.id.nav_users)
}
这将使应用程序导航到另一个片段,并且还将处理工具栏中的标题。
好的,在研究一段时间后,我找到了一个对我有效且不需要太多工作的解决方案。
似乎有两个条件必须满足,才能使其功能类似于您的secondFragment是起始目标。
使用接受帖子中的"ALTERNATIVE"选项。
<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"/>
// Old code
val controller by lazy { findNavController(R.id.nav_host_fragment) }
val appBarConfig by lazy { AppBarConfiguration(controller.graph) }
将其更新以定义一组顶级目标,纠正了在secondFragment上显示返回箭头而不是汉堡菜单图标的问题。
// secondFragment will now show hamburger menu instead of back arrow.
val appBarConfig by lazy { AppBarConfiguration(setOf(R.id.firstFragment, R.id.secondFragment)) }
设置起始目的地可能会对您的项目产生负面影响,因此根据需要进行设置。但在本示例中,我们不需要这样做。如果您希望确保图形具有正确定义的起始片段,则可以按如下方式执行。
controller.graph.startDestination = R.id.secondFragment
startDestination
代码,虽然它可以正常工作,但是导航组件无法恢复片段堆栈。startDestination
来解决了这个问题。
startDestination
是一个空的Fragment(只是一个虚拟的)EmptyFragment
到FirstFragment
的操作需要popUpTo=EmptyFragment
和popUpToInclusive=true
在Activity.onCreate()
中:
if (savedInstanceState == null) {
val navHost = supportFragmentManager.findFragmentById(R.id.nav_host_fragment)!!
val navController = navHost.findNavController()
if (loginComplete) {
navController.navigate(
R.id.action_emptyFragment_to_FirstFragment
)
} else {
navController.navigate(
R.id.action_emptyFragment_to_WelcomeFragment
)
}
}
当 Activity 被重新创建时,savedInstanceState
不为 null,片段会自动恢复。