如何在 Android 上使用 NavigationComponent 打开新片段

发布于 2025-01-13 06:02:03 字数 6659 浏览 1 评论 0原文

在我的应用程序中,我使用单个活动并使用一些片段!
我想首先显示 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

树深时见影 2025-01-20 06:02:03

出于某种原因,看起来 findController() 仍在使用 RegisterFragment 的目标。

我猜测根本原因是因为线程混淆了 findController() ,这使得它仍然认为您位于 RegisterFragment

作为深入研究线程管理之前的解决方法,您可以尝试将操作 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)

Looks like findController() is still using the destination of RegisterFragment for some reason.

My guess the root cause is because the thread confuses findController() which makes it still think you are at RegisterFragment.

As a workaround fix before digging too deep into thread management, you can try to make action actionSplashToHome to a Global Action in your navigation layout file. Just move that action outside the fragment tag, then any fragment in this navigation file can use this action.

<?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>

No need to change your code in Splashfragment, still use findNavController().navigate(R.id.action_global_splash_ToHome)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文