How to scope a view model to a parent fragment?

旧时模样 提交于 2020-07-30 05:55:00

问题


So I'm using the new navigation component (with the one activity principle) and communicating between each fragment using shared view models, however, I've come to a point where I sometimes need to clear the view model but I can't find a good place to clear it. But tbh I think rather than trying to clear it myself, I should really be allowing the framework to do it for me, but it doesn't because the view models are shared and scoped to the activity, but I think I can scope them to a parent fragment, I made a drawing to illustrate what I'm trying to do. so I only want to clear the 2 view models when I navigate back from "Child 1 Child a" currently the view models are never cleared, trying to get the view model currently by calling 'this' in the fragment and getParentFragment in the child doesn't work, can anyone provide an example?

EDIT

It looks like I was already doing something similar but it's not working in my case so I will add some code, here is how I access the first view model in the parent fragment

model = ViewModelProviders.of(this).get(RequestViewModel.class);

and then in the child fragment, I'm doing this

requestViewModel = ViewModelProviders.of(getParentFragment()).get(RequestViewModel.class);

but it's not keeping the data between them, they both have observers attached


回答1:


So, as per @martin's proposed solution derives that even if One/Many Fragments added as Child inside Parent Fragment, Navigation component provides same Fragment manager to both fragments.

Meaning that even if fragments are added as parent-child hierarchy, they'll share same Fragment manager from Navigation component (might be bug in this library ?) & so that ViewModels are not shared due to this dilemma when using getParentFragment() instance for ViewModelProvider inside child fragment.


So, one quick solution to share ViewModels would be getting instance of Parent fragment from Fragment manager using below line for both parent & child fragments :

ViewModelProviders.of(getParentFragment()).get(SharedViewModel.class); // using this in both parent amd child fragment would do the trick !



回答2:


Ok so using this in the parent

model = ViewModelProviders.of(this).get(RequestViewModel.class);

and this in the child

requestViewModel = ViewModelProviders.of(getParentFragment()).get(RequestViewModel.class);

were giving me different hashcodes but the same IDs and it seems to be because of the navigation component, if i change them both to getParentFragment then it works, so i think the component is replacing fragments instead of adding them here, many thanks to @WadeWilson and @JeelVankhede




回答3:


Using Fragment-ktx libr in your app you can get viewModel as below in your app-> build.gradle

 implementation 'androidx.fragment:fragment-ktx:1.1.0'

// get ViewModel in ParentFragment as

 private val viewModel: DemoViewModel by viewModels()

// get same instance of ViewModel in ChildFragment as

 private val viewModel: DemoViewModel by viewModels(
    ownerProducer = { requireParentFragment() }
)



回答4:


Google has given us the ability to scope ViewModel to navigation graphs now. You can use it if you are using the navigation component already. (Personal opinion, you SHOULD move to navigation component if you are not already using it, as it is soo easy to use. Take it from a guy who tried to managed the back stack myself)

You can select all the fragments that need to be grouped together inside the nav graph and right-click->move to nested graph->new graph

now this will move the selected fragments to a nested graph inside the main nav graph like this:

<navigation app:startDestination="@id/homeFragment" ...>
    <fragment android:id="@+id/homeFragment" .../>
    <fragment android:id="@+id/productListFragment" .../>
    <fragment android:id="@+id/productFragment" .../>
    <fragment android:id="@+id/bargainFragment" .../>

    <navigation 
        android:id="@+id/checkout_graph" 
        app:startDestination="@id/cartFragment">

        <fragment android:id="@+id/orderSummaryFragment".../>
        <fragment android:id="@+id/addressFragment" .../>
        <fragment android:id="@+id/paymentFragment" .../>
        <fragment android:id="@+id/cartFragment" .../>

    </navigation>

</navigation>

Now, inside the fragments when you initialize the ViewModel do this

val viewModel: CheckoutViewModel by navGraphViewModels(R.id.checkout_graph)

If you need to pass the viewmodel factory(may be for injecting the viewmodel) you can do it like this:

val viewModel: CheckoutViewModel by navGraphViewModels(R.id.checkout_graph) { viewModelFactory }

Make sure its R.id.graph and not R.navigation.graph

For some reason creating the nav graph and using include to nest it inside the main nav graph was not working for me. Probably it is a bug.

Source: https://medium.com/androiddevelopers/viewmodels-with-saved-state-jetpack-navigation-data-binding-and-coroutines-df476b78144e

Thanks, Manually clearing an Android ViewModel? for pointing me in the right direction.



来源:https://stackoverflow.com/questions/53817046/how-to-scope-a-view-model-to-a-parent-fragment

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!