导航架构组件 - 登录屏幕
Navigation Architecture Component - Login screen
我打算像这样实现导航:
我面临的问题是当用户在 LoginFragmennt
并按下后退按钮时它再次加载 LognFragment
即。陷入循环。
我根据 this 的回答使用条件导航导航到 LoginnFragment
。
如何正确实施?
我可以提出的解决方案之一是在您的 activity onBackPressed 方法中覆盖,如果您当前的目的地(在处理后退之前)是登录片段,则完成 activity。
override fun onBackPressed() {
val currentDestination=NavHostFragment.findNavController(nav_host_fragment).currentDestination
when(currentDestination.id) {
R.id.loginFragment -> {
finish()
}
}
super.onBackPressed()
}
恕我直言,我在我的应用程序中的操作方式更加简洁。只需在导航图中添加这些设置:
<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)
.
当我们导航到 LoginFragment
时,popUpTo 和 popUpToInclusive 关闭 ProfileFragment
因此,如果用户导航回来,它会退出应用程序。
这是 Ian Lake 在 Navigating navigation video at Jul 23, 2020 on Android Developers YouTube channel. The solution is based on navigation 2.3 release which introduced an ability to return a result 到上一个目的地建议的官方解决方案。
在我们的例子中,登录片段 returns 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
状态并对其进行处理。请注意,在登录片段将结果放入并 return 返回配置文件片段之前,不会调用观察者 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
})
})
}
}
}
}
我打算像这样实现导航:
我面临的问题是当用户在 LoginFragmennt
并按下后退按钮时它再次加载 LognFragment
即。陷入循环。
我根据 this 的回答使用条件导航导航到 LoginnFragment
。
如何正确实施?
我可以提出的解决方案之一是在您的 activity onBackPressed 方法中覆盖,如果您当前的目的地(在处理后退之前)是登录片段,则完成 activity。
override fun onBackPressed() {
val currentDestination=NavHostFragment.findNavController(nav_host_fragment).currentDestination
when(currentDestination.id) {
R.id.loginFragment -> {
finish()
}
}
super.onBackPressed()
}
恕我直言,我在我的应用程序中的操作方式更加简洁。只需在导航图中添加这些设置:
<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)
.
当我们导航到 LoginFragment
时,popUpTo 和 popUpToInclusive 关闭 ProfileFragment
因此,如果用户导航回来,它会退出应用程序。
这是 Ian Lake 在 Navigating navigation video at Jul 23, 2020 on Android Developers YouTube channel. The solution is based on navigation 2.3 release which introduced an ability to return a result 到上一个目的地建议的官方解决方案。
在我们的例子中,登录片段 returns 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
状态并对其进行处理。请注意,在登录片段将结果放入并 return 返回配置文件片段之前,不会调用观察者 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
})
})
}
}
}
}