简体   繁体   English

如何在 Kotlin 中使用带 Livedata 的 MVVM 模式从子片段访问父片段数据

[英]How to access Parent Fragment data from Child Fragment using MVVM pattern with Livedata in Kotlin

I have a list Fragment where recyclerview is used to displayed the list when the use presses on the list item user goes to new fragment(parent fragment) where i have setup a tablayout with 2 fragments(detail fragment and second fragment) using viewpager2.我有一个列表片段,当用户按下列表项用户转到新片段(父片段)时,recyclerview 用于显示列表,我在其中使用 viewpager2 设置了带有 2 个片段(详细片段和第二片段)的 tablayout。 Here the data from list item is passed to parent fragment using Bundle.这里使用 Bundle 将列表项中的数据传递给父片段。 i need to get access this data which is available in parent fragment and use it to display data in child fragment(detail fragment)我需要访问父片段中可用的数据,并使用它来显示子片段(详细片段)中的数据

i am using hilt as dependency injection.我正在使用 hilt 作为依赖注入。

list fragment code to send data列表片段代码发送数据

 private val mylistsViewModel by viewModels<MyListsViewModel>()
    private var mylists: MyListsResponse? = null
.......
`    private fun onItemClicked(mylistsResponse: MyListsResponse) {
        val bundle = Bundle()
        bundle.putString("mylist",Gson().toJson(mylistsResponse))
        findNavController().navigate(R.id.action_mylistsFragment_to_parentFragment,bundle)
    }`

accessing it in parent fragment在父片段中访问它

 private val mylistsViewModel by viewModels<MyListsViewModel>()
    private var mylists: MyListsResponse? = null
.......

        val jsonMyLists = arguments?.getString("mylist")
        if (jsonMyLists != null) {
            mylists = Gson().fromJson<MyListsResponse>(jsonMyLists, MyListsResponse::class.java)
            mylists?.let {


            }
        }

UI Flow界面流程

access data from parent to child fragment从父片段访问数据到子片段

Can any one kindly suggest on how to resolve this issue.任何人都可以建议如何解决这个问题。

i refered this googleDocs我参考了这个googleDocs

and tried to access the data in details fragment by refering to this code并尝试通过参考此代码访问详细信息片段中的数据

class ListFragment: Fragment() {
    // Using the viewModels() Kotlin property delegate from the fragment-ktx
    // artifact to retrieve the ViewModel
    private val viewModel: ListViewModel by viewModels()
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        viewModel.filteredList.observe(viewLifecycleOwner, Observer { list ->
            // Update the list UI
        }
    }
}

class ChildFragment: Fragment() {
    // Using the viewModels() Kotlin property delegate from the fragment-ktx
    // artifact to retrieve the ViewModel using the parent fragment's scope
    private val viewModel: ListViewModel by viewModels({requireParentFragment()})
    ...
}

But when i go to detail fragment the app is crashing and giving error as但是当我 go 详细说明应用程序崩溃并给出错误时

"Fragment DetailsFragment is not a child Fragment, it is directly attached to dagger.hilt.android.internal.managers" “Fragment DetailsFragment 不是子 Fragment,它直接附加到 dagger.hilt.android.internal.managers”

The problem is when you are using the Navigation component to handle navigation in your application requireParentFragment will return an instance of the NavHostFragment which is not ListFragment as you expected.问题是当您使用 Navigation 组件处理应用程序中的导航时,requireParentFragment 将返回requireParentFragment的一个实例,它ListFragment NavHostFragment Fortunately, there is a way to instantiate a view model using the navigation component backStackEntry and Hilt as follows:幸运的是,有一种方法可以使用导航组件backStackEntry和 Hilt 实例化视图 model,如下所示:

@AndroidEntryPoint
class ListFragment: Fragment() {
    // Using the viewModels() Kotlin property delegate from the fragment-ktx
    // artifact to retrieve the ViewModel
    private val viewModel: ListViewModel by lazy {
        val destination = findNavController().currentBackStackEntry!!.destination

        val backStackEntry = findNavController().getBackStackEntry(destination.id)

        val storeProducer: () -> ViewModelStore = {
            backStackEntry.viewModelStore
        }

        createViewModelLazy(
            ListViewModel::class, storeProducer,
            factoryProducer = {
                HiltViewModelFactory(requireActivity(), backStackEntry)
            }
        ).value
    }
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        viewModel.filteredList.observe(viewLifecycleOwner, Observer { list ->
            // Update the list UI
        }
    }
}

@AndroidEntryPoint
class ChildFragment: Fragment() {
    // Using the viewModels() Kotlin property delegate from the fragment-ktx
    // artifact to retrieve the ViewModel using the parent fragment's scope
    private val viewModel: ListViewModel by lazy {
        val destination = findNavController().previousBackStackEntry!!.destination

        val backStackEntry = findNavController().getBackStackEntry(destination.id)

        val storeProducer: () -> ViewModelStore = {
            backStackEntry.viewModelStore
        }

        createViewModelLazy(
            ListViewModel::class, storeProducer,
            factoryProducer = {
                HiltViewModelFactory(requireActivity(), backStackEntry)
            }
        ).value
    }
    ...
}

Also, don't forget to annotate the ListViewModel with @HiltViewModel .另外,不要忘记使用@HiltViewModel注释ListViewModel

This way of implementation can work with any fragment and the previous one as you may notice in the ChildFragment we are using previousBackStackEntry instead of currentBackStackEntry in the ListFragment这种实现方式适用于任何片段和前一个片段,您可能会注意到我们在ChildFragment中使用的是previousBackStackEntry而不是ListFragment中的currentBackStackEntry

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM