如何在 Android 上使用 NavigationComponent 打开新片段
在我的应用程序中,我使用单个活动并使用一些片段!
我想首先显示 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$1$1.emit(SplashFragment.kt:43)
at my.app.ui.splash.SplashFragment$onViewCreated$1$1.emit(SplashFragment.kt:38)
at my.app.utils.UserInfo$getUserToken$$inlined$map$1$2.emit(Emitters.kt:224)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(SafeCollector.kt:15)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.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$1$invokeSuspend$$inlined$map$1$2.emit(Collect.kt:137)
at kotlinx.coroutines.flow.FlowKt__LimitKt$dropWhile$1$1.emit(Limit.kt:37)
at kotlinx.coroutines.flow.StateFlowImpl.collect(StateFlow.kt:398)
at kotlinx.coroutines.flow.StateFlowImpl$collect$1.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>
我该如何修复它?
In my application I used single activity and use some fragments!
I want first of all show SplashFragment and checked user token, if exists open HomeFragment else open RegisterFragment!
I write below codes, but after register user show me below error!
Logcat error :
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$1$1.emit(SplashFragment.kt:43)
at my.app.ui.splash.SplashFragment$onViewCreated$1$1.emit(SplashFragment.kt:38)
at my.app.utils.UserInfo$getUserToken$inlined$map$1$2.emit(Emitters.kt:224)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(SafeCollector.kt:15)
at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.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$1$invokeSuspend$inlined$map$1$2.emit(Collect.kt:137)
at kotlinx.coroutines.flow.FlowKt__LimitKt$dropWhile$1$1.emit(Limit.kt:37)
at kotlinx.coroutines.flow.StateFlowImpl.collect(StateFlow.kt:398)
at kotlinx.coroutines.flow.StateFlowImpl$collect$1.invokeSuspend(Unknown Source:15)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
RegisterFragment codes:
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 codes:
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)
}
}
}
}
Navigation's codes:
<?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>
How can I fix it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
出于某种原因,看起来
findController()
仍在使用RegisterFragment
的目标。我猜测根本原因是因为线程混淆了
findController()
,这使得它仍然认为您位于RegisterFragment
。作为深入研究线程管理之前的解决方法,您可以尝试将操作
actionSplashToHome
设置为导航布局文件中的全局操作
。只需将该操作移到fragment
标记之外,然后该导航文件中的任何片段都可以使用该操作。无需更改 Splashfragment 中的代码,仍然使用
findNavController().navigate(R.id.action_global_splash_ToHome)
Looks like
findController()
is still using the destination ofRegisterFragment
for some reason.My guess the root cause is because the thread confuses
findController()
which makes it still think you are atRegisterFragment
.As a workaround fix before digging too deep into thread management, you can try to make action
actionSplashToHome
to aGlobal Action
in your navigation layout file. Just move that action outside thefragment
tag, then any fragment in this navigation file can use this action.No need to change your code in Splashfragment, still use
findNavController().navigate(R.id.action_global_splash_ToHome)