使用导航的操作而不是直接片段 ID 会使应用程序崩溃

Using Navigation's actions instead of direct fragment id crashes app

我在使用导航组件执行一项非常简单的任务时遇到了问题。 我只有 2 个屏幕:MainFragment 和 SearchFragment。

当我尝试通过导航操作从 MainFragment 转到 SearchFragment 时,它工作得很好。然后我按下后退按钮,它自然会回到 MainFragment。

问题是,当我第二次单击同一个按钮再次转到 SearchFragment 时,我收到以下错误:

java.lang.IllegalArgumentException: Navigation action/destination action_mainFragment_to_searchFragment cannot be found from the current destination Destination(searchFragment)

我正在从 MainFragment 导航到搜索,如下所示:

findNavController().navigate(MainFragmentDirections.actionMainFragmentToSearchFragment())

我尝试重做 nav_graph.xml 但没有成功。

如果我直接使用 id 导航,它工作正常,我可以来回多次

findNavController().navigate(R.id.searchFragment)

关于如何解决安全参数问题的任何想法?

编辑:

这是我的nav_graph

<?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_graph_main"
    app:startDestination="@id/mainFragment">

    <fragment
        android:id="@+id/mainFragment"
        android:name="news.presentation.main.MainFragment"
        android:label="fragment_main"
        tools:layout="@layout/fragment_main">
        <action
            android:id="@+id/action_mainFragment_to_searchFragment"
            app:destination="@id/searchFragment" />
    </fragment>
    <fragment
        android:id="@+id/searchFragment"
        android:name="news.presentation.search.SearchFragment"
        android:label="fragment_search"
        tools:layout="@layout/fragment_search" />
</navigation>

这是activity(它基本上只是片段的容器):

这是activity_home.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView 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/homeFragContainer"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:defaultNavHost="true"
    app:navGraph="@navigation/nav_graph_main"
    tools:context=".presentation.HomeActivity" />

这是 HomeActivity.kt

@AndroidEntryPoint
class HomeActivity : AppCompatActivity(R.layout.activity_home)

这是 HomeFragment:

@AndroidEntryPoint
class MainFragment : Fragment(R.layout.fragment_main) {

    private val binding by viewBinding(FragmentMainBinding::bind)
    private val viewModel by viewModels<MainViewModel>()

    private val articlesAdapter = ArticlesAdapter(::onSubscriptionClicked)
    private lateinit var layoutManager: LinearLayoutManager

    override fun onViewCreated(view: View, bundle: Bundle?) {
        super.onViewCreated(view, bundle)
        setupViews()
        setupViewModel()
        setupRecyclerView()
    }

    private fun setupViews() {
        binding.toolbar.title = getString(R.string.app_name)
        binding.fab.setOnClickListener {
            viewModel.intent.offer(MainViewModel.Intent.SearchClicked)
        }
    }

    private fun setupViewModel() {
        viewModel.state
            .onEach(::handleState)
            .launchIn(lifecycleScope)
        viewModel.feedFlow
            .onEach(articlesAdapter::submitList)
            .launchIn(lifecycleScope)
    }

    private fun setupRecyclerView() {
        layoutManager = LinearLayoutManager(requireContext())
        binding.recycler.layoutManager = layoutManager
        binding.recycler.adapter = articlesAdapter
        binding.recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                val canScroll = recyclerView.canScrollVertically(-1)
                binding.toolbar.isLifted = canScroll
            }
        })
    }

    private fun onSubscriptionClicked(article: Article) {
        viewModel.intent.offer(MainViewModel.Intent.ItemClicked(article))
    }

    private fun handleState(state: MainViewModel.State) = when (state) {
        NavigateToSearch -> findNavController().navigate(MainFragmentDirections.actionMainFragmentToSearchFragment())
        is FeedReady -> binding.progress.isVisible = false
        Loading -> binding.progress.isVisible = true
        is NavigateToArticle -> {
            // works
            // findNavController().navigate(
            //     R.id.articleFragment,
            //     bundleOf("articleLink" to state.link)
            // )
        }
    }
}

这是它的XML:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="false"
    android:clipToPadding="false"
    tools:context=".presentation.main.MainFragment">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@id/bottomNav"
        app:layout_constraintTop_toBottomOf="@id/toolbar"
        tools:listitem="@layout/item_article_big" />

    <com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/standard"
        android:text="Add Feed"
        android:textAlignment="center"
        app:icon="@drawable/ic_add"
        app:layout_constraintBottom_toTopOf="@id/bottomNav"
        app:layout_constraintEnd_toEndOf="parent" />

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/bottomNav"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:menu="@menu/menu_main" />

    <tgo1014.news.presentation.customview.LiftableToolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clipChildren="false"
        android:clipToPadding="false"
        app:layout_constraintTop_toTopOf="parent" />

    <com.google.android.material.progressindicator.LinearProgressIndicator
        android:id="@+id/progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:indeterminate="true"
        app:layout_constraintBottom_toBottomOf="@id/toolbar"
        app:layout_constraintTop_toBottomOf="@id/toolbar"
        tools:visibility="visible" />

</androidx.constraintlayout.widget.ConstraintLayout>

错误是由于生命周期造成的。 替换为:

viewModel.state
    .onEach(::handleState)
    .launchIn(lifecycleScope)

来自

viewModel.state
    .onEach(::handleState)
    .launchIn(viewLifecycleOwner.lifecycleScope)