如何在 Android 上使用导航组件打开新片段
How to open new fragment with NavigationComponent on Android
在我的应用程序中,我使用了单个 activity 并使用了一些片段!
我首先要显示 SplashFragment 并检查用户令牌,如果存在则打开 HomeFragment 否则打开 RegisterFragment!
我写了下面的代码,但是注册用户后显示下面的错误!
Logcat 错误:
java.lang.IllegalArgumentException: Navigation action/destination my.app:id/actionSplashToHome cannot be found from the current destination Destination(my.app:id/registerFragment) label=fragment_register class=my.app.ui.register.RegisterFragment
at androidx.navigation.NavController.navigate(NavController.kt:1536)
at androidx.navigation.NavController.navigate(NavController.kt:1468)
at androidx.navigation.NavController.navigate(NavController.kt:1450)
at androidx.navigation.NavController.navigate(NavController.kt:1433)
at my.app.ui.splash.SplashFragment$onViewCreated.emit(SplashFragment.kt:43)
at my.app.ui.splash.SplashFragment$onViewCreated.emit(SplashFragment.kt:38)
at my.app.utils.UserInfo$getUserToken$$inlined$map.emit(Emitters.kt:224)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun.invoke(SafeCollector.kt:15)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun.invoke(SafeCollector.kt:15)
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:77)
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:59)
at androidx.datastore.core.SingleProcessDataStore$data$invokeSuspend$$inlined$map.emit(Collect.kt:137)
at kotlinx.coroutines.flow.FlowKt__LimitKt$dropWhile.emit(Limit.kt:37)
at kotlinx.coroutines.flow.StateFlowImpl.collect(StateFlow.kt:398)
at kotlinx.coroutines.flow.StateFlowImpl$collect.invokeSuspend(Unknown Source:15)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
RegisterFragment代码:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//InitViews
binding.apply {
//Submit
submitBtn.setOnClickListener { it ->
val name = nameEdt.text.toString()
val email = emailEdt.text.toString()
val password = passwordEdt.text.toString()
//Validation
if (name.isNotEmpty() || email.isNotEmpty() || password.isNotEmpty()) {
body.name = name
body.email = email
body.password = password
viewModel.registerUser(body)
viewModel.registerUser.observe(viewLifecycleOwner) { itResponse ->
Log.e("UserInfoLog","1 : "+itResponse.email.toString())
lifecycle.coroutineScope.launchWhenCreated {
userDataStore.saveUserToken(itResponse.email.toString())
Log.e("UserInfoLog","2 : "+itResponse.email.toString())
findNavController().navigateUp()
}
}
} else {
Snackbar.make(it, getString(R.string.fillAllFields), Snackbar.LENGTH_SHORT).show()
}
SplashFragment 代码:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//Check user token
lifecycle.coroutineScope.launchWhenCreated {
delay(2000)
userDataStore.getUserToken().collect {
if (it.isEmpty()) {
findNavController().navigate(R.id.actionSplashToRegister)
} else {
findNavController().navigate(R.id.actionSplashToHome)
}
}
}
}
导航代码:
<?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/nav_main"
app:startDestination="@id/splashFragment">
<fragment
android:id="@+id/splashFragment"
android:name="my.app.ui.splash.SplashFragment"
android:label="fragment_splash"
tools:layout="@layout/fragment_splash">
<action
android:id="@+id/actionSplashToRegister"
app:destination="@id/registerFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<action
android:id="@+id/actionSplashToHome"
app:destination="@id/homeFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
</fragment>
<fragment
android:id="@+id/registerFragment"
android:name="my.app.ui.register.RegisterFragment"
android:label="fragment_register"
tools:layout="@layout/fragment_register">
<action
android:id="@+id/actionRegisterToHome"
app:destination="@id/homeFragment" />
</fragment>
<fragment
android:id="@+id/homeFragment"
android:name="my.app.ui.home.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/action_homeFragment_to_detailFragment"
app:destination="@id/detailFragment" />
</fragment>
<fragment
android:id="@+id/favoriteFragment"
android:name="my.app.ui.favorite.FavoriteFragment"
android:label="fragment_favorite"
tools:layout="@layout/fragment_favorite" />
<fragment
android:id="@+id/searchFragment"
android:name="my.app.ui.search.SearchFragment"
android:label="fragment_search"
tools:layout="@layout/fragment_search" />
<fragment
android:id="@+id/detailFragment"
android:name="my.app.ui.detail.DetailFragment"
android:label="fragment_detail"
tools:layout="@layout/fragment_detail" />
</navigation>
我该如何解决?
看起来 findController()
出于某种原因仍在使用 RegisterFragment
的目的地。
我猜根本原因是因为线程混淆了 findController()
,这使得它仍然认为你在 RegisterFragment
。
作为深入研究线程管理之前的解决方法,您可以尝试对导航布局文件中的 Global Action
执行 actionSplashToHome
操作。只需将该操作移到 fragment
标记之外,然后此导航文件中的任何片段都可以使用此操作。
<?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/nav_main"
app:startDestination="@id/splashFragment">
<fragment
android:id="@+id/splashFragment"
android:name="my.app.ui.splash.SplashFragment"
android:label="fragment_splash"
tools:layout="@layout/fragment_splash">
<action
android:id="@+id/actionSplashToRegister"
app:destination="@id/registerFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
</fragment>
<action
android:id="@+id/action_global_splash_ToHome"
app:destination="@id/homeFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
<fragment
android:id="@+id/registerFragment"
android:name="my.app.ui.register.RegisterFragment"
android:label="fragment_register"
tools:layout="@layout/fragment_register">
<action
android:id="@+id/actionRegisterToHome"
app:destination="@id/homeFragment" />
</fragment>
<fragment
android:id="@+id/homeFragment"
android:name="my.app.ui.home.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/action_homeFragment_to_detailFragment"
app:destination="@id/detailFragment" />
</fragment>
<fragment
android:id="@+id/favoriteFragment"
android:name="my.app.ui.favorite.FavoriteFragment"
android:label="fragment_favorite"
tools:layout="@layout/fragment_favorite" />
<fragment
android:id="@+id/searchFragment"
android:name="my.app.ui.search.SearchFragment"
android:label="fragment_search"
tools:layout="@layout/fragment_search" />
<fragment
android:id="@+id/detailFragment"
android:name="my.app.ui.detail.DetailFragment"
android:label="fragment_detail"
tools:layout="@layout/fragment_detail" />
</navigation>
无需更改 Splashfragment 中的代码,仍然使用 findNavController().navigate(R.id.action_global_splash_ToHome)
在我的应用程序中,我使用了单个 activity 并使用了一些片段!
我首先要显示 SplashFragment 并检查用户令牌,如果存在则打开 HomeFragment 否则打开 RegisterFragment!
我写了下面的代码,但是注册用户后显示下面的错误!
Logcat 错误:
java.lang.IllegalArgumentException: Navigation action/destination my.app:id/actionSplashToHome cannot be found from the current destination Destination(my.app:id/registerFragment) label=fragment_register class=my.app.ui.register.RegisterFragment
at androidx.navigation.NavController.navigate(NavController.kt:1536)
at androidx.navigation.NavController.navigate(NavController.kt:1468)
at androidx.navigation.NavController.navigate(NavController.kt:1450)
at androidx.navigation.NavController.navigate(NavController.kt:1433)
at my.app.ui.splash.SplashFragment$onViewCreated.emit(SplashFragment.kt:43)
at my.app.ui.splash.SplashFragment$onViewCreated.emit(SplashFragment.kt:38)
at my.app.utils.UserInfo$getUserToken$$inlined$map.emit(Emitters.kt:224)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun.invoke(SafeCollector.kt:15)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun.invoke(SafeCollector.kt:15)
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:77)
at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:59)
at androidx.datastore.core.SingleProcessDataStore$data$invokeSuspend$$inlined$map.emit(Collect.kt:137)
at kotlinx.coroutines.flow.FlowKt__LimitKt$dropWhile.emit(Limit.kt:37)
at kotlinx.coroutines.flow.StateFlowImpl.collect(StateFlow.kt:398)
at kotlinx.coroutines.flow.StateFlowImpl$collect.invokeSuspend(Unknown Source:15)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
RegisterFragment代码:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//InitViews
binding.apply {
//Submit
submitBtn.setOnClickListener { it ->
val name = nameEdt.text.toString()
val email = emailEdt.text.toString()
val password = passwordEdt.text.toString()
//Validation
if (name.isNotEmpty() || email.isNotEmpty() || password.isNotEmpty()) {
body.name = name
body.email = email
body.password = password
viewModel.registerUser(body)
viewModel.registerUser.observe(viewLifecycleOwner) { itResponse ->
Log.e("UserInfoLog","1 : "+itResponse.email.toString())
lifecycle.coroutineScope.launchWhenCreated {
userDataStore.saveUserToken(itResponse.email.toString())
Log.e("UserInfoLog","2 : "+itResponse.email.toString())
findNavController().navigateUp()
}
}
} else {
Snackbar.make(it, getString(R.string.fillAllFields), Snackbar.LENGTH_SHORT).show()
}
SplashFragment 代码:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//Check user token
lifecycle.coroutineScope.launchWhenCreated {
delay(2000)
userDataStore.getUserToken().collect {
if (it.isEmpty()) {
findNavController().navigate(R.id.actionSplashToRegister)
} else {
findNavController().navigate(R.id.actionSplashToHome)
}
}
}
}
导航代码:
<?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/nav_main"
app:startDestination="@id/splashFragment">
<fragment
android:id="@+id/splashFragment"
android:name="my.app.ui.splash.SplashFragment"
android:label="fragment_splash"
tools:layout="@layout/fragment_splash">
<action
android:id="@+id/actionSplashToRegister"
app:destination="@id/registerFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<action
android:id="@+id/actionSplashToHome"
app:destination="@id/homeFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
</fragment>
<fragment
android:id="@+id/registerFragment"
android:name="my.app.ui.register.RegisterFragment"
android:label="fragment_register"
tools:layout="@layout/fragment_register">
<action
android:id="@+id/actionRegisterToHome"
app:destination="@id/homeFragment" />
</fragment>
<fragment
android:id="@+id/homeFragment"
android:name="my.app.ui.home.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/action_homeFragment_to_detailFragment"
app:destination="@id/detailFragment" />
</fragment>
<fragment
android:id="@+id/favoriteFragment"
android:name="my.app.ui.favorite.FavoriteFragment"
android:label="fragment_favorite"
tools:layout="@layout/fragment_favorite" />
<fragment
android:id="@+id/searchFragment"
android:name="my.app.ui.search.SearchFragment"
android:label="fragment_search"
tools:layout="@layout/fragment_search" />
<fragment
android:id="@+id/detailFragment"
android:name="my.app.ui.detail.DetailFragment"
android:label="fragment_detail"
tools:layout="@layout/fragment_detail" />
</navigation>
我该如何解决?
看起来 findController()
出于某种原因仍在使用 RegisterFragment
的目的地。
我猜根本原因是因为线程混淆了 findController()
,这使得它仍然认为你在 RegisterFragment
。
作为深入研究线程管理之前的解决方法,您可以尝试对导航布局文件中的 Global Action
执行 actionSplashToHome
操作。只需将该操作移到 fragment
标记之外,然后此导航文件中的任何片段都可以使用此操作。
<?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/nav_main"
app:startDestination="@id/splashFragment">
<fragment
android:id="@+id/splashFragment"
android:name="my.app.ui.splash.SplashFragment"
android:label="fragment_splash"
tools:layout="@layout/fragment_splash">
<action
android:id="@+id/actionSplashToRegister"
app:destination="@id/registerFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
</fragment>
<action
android:id="@+id/action_global_splash_ToHome"
app:destination="@id/homeFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
<fragment
android:id="@+id/registerFragment"
android:name="my.app.ui.register.RegisterFragment"
android:label="fragment_register"
tools:layout="@layout/fragment_register">
<action
android:id="@+id/actionRegisterToHome"
app:destination="@id/homeFragment" />
</fragment>
<fragment
android:id="@+id/homeFragment"
android:name="my.app.ui.home.HomeFragment"
android:label="fragment_home"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/action_homeFragment_to_detailFragment"
app:destination="@id/detailFragment" />
</fragment>
<fragment
android:id="@+id/favoriteFragment"
android:name="my.app.ui.favorite.FavoriteFragment"
android:label="fragment_favorite"
tools:layout="@layout/fragment_favorite" />
<fragment
android:id="@+id/searchFragment"
android:name="my.app.ui.search.SearchFragment"
android:label="fragment_search"
tools:layout="@layout/fragment_search" />
<fragment
android:id="@+id/detailFragment"
android:name="my.app.ui.detail.DetailFragment"
android:label="fragment_detail"
tools:layout="@layout/fragment_detail" />
</navigation>
无需更改 Splashfragment 中的代码,仍然使用 findNavController().navigate(R.id.action_global_splash_ToHome)