Android 如何以编程方式更改导航图的起始目标[Jetpack]
基本上,我有以下导航图: 我想在到达后立即将导航图中的起始点更改为Android 如何以编程方式更改导航图的起始目标[Jetpack],android,android-jetpack,android-navigation,android-architecture-navigation,Android,Android Jetpack,Android Navigation,Android Architecture Navigation,基本上,我有以下导航图: 我想在到达后立即将导航图中的起始点更改为片段2(以防止在按下后退按钮时返回到片段1,就像启动屏幕一样) 这是我的代码: navGraph = navController.getGraph(); navGraph.setStartDestination(R.id.fragment2); navController.setGraph(navGraph); 但是,很明显,它不工作了,在按下后退按钮后,它返回到片段1 我做错了吗? 还有其他解决方案吗?更新: 当您有如下导航
片段2
(以防止在按下后退按钮时返回到片段1
,就像启动屏幕一样)
这是我的代码:
navGraph = navController.getGraph();
navGraph.setStartDestination(R.id.fragment2);
navController.setGraph(navGraph);
但是,很明显,它不工作了,在按下后退按钮后,它返回到片段1
我做错了吗?
还有其他解决方案吗?更新:
当您有如下导航图时:
<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"/>
并将其用于导航:
Navigation.findNavController(view).navigate(R.id.action_firstFragment_to_secondFragment, bundle, navOptions);
setPoputo(int destinationId,含布尔值)
-导航前弹出到给定目标。这将从后堆栈中弹出所有不匹配的目标,直到找到该目标
destinationId
-要弹出的目的地,清除所有中间目的地
inclusive
-如果为true,则也会从后堆栈中弹出给定的目标
备选方案: 旧答案 已弃用:已弃用
NavOptions
中操作和关联API的clearTask
属性
资料来源:
如果您想将根片段更改为
fragment 2
(例如,按下fragment 2
上的“后退”按钮后,您将退出应用程序),您应该将下一个属性添加到操作
或目的地
:
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"/>
我找到了解决这个问题的办法,但很难看。我想这在alpha库中是意料之中的,但我希望谷歌能够简化/修复这一问题,因为这是一种非常流行的导航模式 亚历克赛的解决方案对我不起作用。我的问题是,我的操作栏上显示了向上箭头,方法是:
NavigationUI.setupActionBarWithNavController(此,navController)
如果我按照上面Alexey的建议去做,我的新起始片段仍然有一个箭头指向我的初始起始片段。如果我按下那个向上箭头,我的应用程序就会重新启动,转换到自身(新的开始片段)
下面是实现我想要的功能所需的代码:
- 片段#1是我的应用程序最初启动的地方
- 我可以在片段#1中执行身份验证检查,然后通过编程将开始更改为片段#2
- 进入片段#2后,没有向上箭头,按下后退按钮不会进入片段#1
// 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)
然后根据Alexey的建议,在片段1的onActivityCreated中:
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)
}
}
覆盖活动创建的乐趣(savedInstanceState:Bundle?){
...
//检查用户身份验证
if(sharedViewModel.isUserAuthenticated()){
(活动作为MainActivity)。makeHomeStart()//您实际上不需要弹出启动片段。它可以在应用程序的剩余生命中一直保留在那里。您应该做的是从启动屏幕确定要显示的下一个屏幕
在上图中,您可以在初始屏幕状态下询问是否存在已保存的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)
您也可以尝试以下方法
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)
}
与其尝试弹出开始目的地或手动导航到目标目的地,不如使用另一个具有不同工作流的导航图。
如果您希望有条件地使用完全不同的导航流,则这一点会更好。在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>
?xml version=“1.0”encoding=“utf-8”>
好吧,在处理了一段时间后,我找到了一个适合我的解决方案,它不需要大量的工作
似乎有两件事必须到位,它才能正常工作,就好像第二个片段是您的出发点和目的地一样
在接受的帖子中使用替代选项
<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"/>
更新它以定义一组顶级目的地纠正了在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
注意:设置此选项不会阻止返回箭头出现在secondFragment中,并且我发现返回箭头似乎对导航没有影响。对于具有类似内容的导航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>
这将使应用程序导航到另一个片段,并在工具栏中处理标题。按片段2上的“后退”按钮后,您想实现什么?我想退出应用程序,在这种情况下,可以。查看以获取。我甚至不知道您可以通过编程方式设置图表,谢谢。这个问题帮助了我一个小时很多!不知道你的代码为什么不工作。我正在onCreate中设置它,没有问题。你问题中的代码解决了我的问题:)thnx!clearTask标志和setClearTask从alpha02开始就被弃用了,看看这些,这对我不起作用。你把代码放在哪里?像你一样,我有一个欢迎片段,被设置为home fragment、 在welcome片段的onActivityCreated方法中,我获取我的viewmodel,检查用户是否已登录,如果已登录,我从上面尝试了您的解决方案。在第二个(新主页)片段上仍然有一个向上箭头。我如何才能
?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>
<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 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
<?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)
}