导航架构组件 - 登录界面

8
我计划实现这样的导航:
enter image description here
我面临的问题是当用户在 LoginFragment 页面按返回按钮时,它会再次加载 LoginFragment,即陷入循环中。
我使用条件导航来导航到 LoginFragment,具体实现请参考此答案
如何正确实现呢?

你陷入了循环中,因为在按返回键时,你会返回到先前的片段,但由于相同的条件仍然存在(例如用户未登录),所以你会导航到LoginFragment。你的情况是什么?你希望在按返回键时导航到哪里? - Alex
当用户按下返回按钮时,我想关闭应用程序。 - Yaswant Narayan
就像其他应用程序一样,在第一次遇到登录界面并按返回按钮时,该应用会关闭。 - Yaswant Narayan
尝试这个解决方案 https://dev59.com/X63la4cB1Zd3GeqPJ0BD#51589910 - Alex
1
官方文档中有关于这个问题的文章 https://developer.android.com/guide/navigation/navigation-conditional - RonaldPaguay
3个回答

11

我认为在我的应用程序中如何执行它会更加简洁。只需在导航图中添加以下设置:

<fragment
    android:id="@+id/profile_dest"
    android:name="com.example.ProfileFragment">
    <action
        android:id="@+id/action_profile_dest_to_login_dest"
        app:destination="@id/login_dest"
        app:popUpTo="@+id/profile_dest"
        app:popUpToInclusive="true" />       
</fragment>

然后通过 findNavController().navigate(R.id.action_profile_dest_to_login_dest) 导航到登录页面。

popUpTopopUpToInclusive 在导航到 LoginFragment 页面时关闭了 ProfileFragment 页面,所以如果用户返回,应用程序将退出。


2
是的,完全正确。就像问题中所示的那样。此操作还应关闭(从后堆栈中删除)登录屏幕。 - Carson Holzheimer
所以我用这种方式实现了我的导航,它似乎可以工作,但是现在当我转到条件屏幕(在我的情况下登录屏幕)并成功登录后返回导航(比如原始帖子中的ProfileFragment),并旋转屏幕时,我的应用程序会崩溃,显示错误:IllegalStateException("unknown destination during restore: id/profile_dest")。不确定为什么会出现这种情况。 - sbearben
1
你用了什么代码来返回?你应该使用相同的过程,包括 popUpTo="@+id/login_dest"popUpToInclusive="true"。不要使用 popBackStack() - Carson Holzheimer
你可能已经做了这件事。就我所知,我还没有遇到这个问题。请确保您使用的是最新版本的导航系统,并发布另一个问题,以便我们进行查看。如果您认为这是一个错误,请在 Google 的问题跟踪器中添加一个问题。 - Carson Holzheimer
这个问题在1.0.0-beta01中显然已经被修复了。 - Carson Holzheimer
显示剩余2条评论

6

我可以提出的一种解决方案是,在您的活动中覆盖onBackPressed方法,并在当前目标(在处理后退之前)为登录片段时完成该活动。

override fun onBackPressed() {
    val currentDestination=NavHostFragment.findNavController(nav_host_fragment).currentDestination
    when(currentDestination.id) {
        R.id.loginFragment -> {
            finish()
        }
    }
    super.onBackPressed()
}

这个解决方案可行。现在我有一个疑问。关闭一个activity,我应该使用 exit(0) 还是 finish()?这两者之间有什么区别? - Yaswant Narayan
4
exit(0) 会终止你的应用程序,而 finish() 则是结束你的活动。不要使用 exit(0)。 - Alex

2
这是Ian Lake在2020年7月23日《导航视频中的导航》中在Android Developers YouTube频道上提出的官方解决方案。该解决方案基于导航2.3版本,该版本引入了返回结果到先前的目标的功能。
在我们的情况下,登录片段会向先前的目标返回LOGIN_SUCCESSFUL状态,可能是个人资料片段或任何需要登录的其他片段。
class LoginFragment : Fragment(R.layout.login) {
    ...

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val navController = findNavController()
        val savedStateHandle = navController.previousBackStackEntry?.savedStateHandle
            ?: throw IllegalStateException("the login fragment must not be a start destination")
            
        savedStateHandle.set(LOGIN_SUCCESSFUL, false)
        // Hook up your UI, ask for login
        
        userRepository.addLoginSuccessListener {
            savedStateHandle.set(LOGIN_SUCCESSFUL, true)
            navController.popBackStack()
        } 
    }
}

该个人资料片段订阅了LOGIN_SUCCESSFUL状态并对其进行处理。请注意,观察者lambda表达式只有在登录片段放置结果并返回到个人资料片段后才会被调用。
class ProfileFragment : Fragment(R.layout.profile) {
    ...
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val navController = findNavController()
        viewLifecycleOwner.lifecycleScope.launchWhenStarted {
            userRepository.userFlow.collect { user -> 
                if (user == null) {
                    navController.navigate(R.id.login)
                }
            }
        }
        
        val savedStateHandle = navController.currentBackStackEntry?.savedStateHandle
            ?: throw IllegalStateException()
        
        savedStateHandle.getLiveData<Boolean>(LOGIN_SUCCESSFUL)
            .observe(viewLifecycleOwner) { success -> 
                if (!success) {
                    // do whathever we want, just for an example go to
                    // the start destination which doesn't require login
                    val startDestination = navController.graph.startDestination
                    navController.navigate(startDestination, navOptions {
                        popUpTo(startDestination {
                            inclusive = true
                        })
                    })
                }
            }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接