问题
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