Fragment Inheritance with ViewModel Inheritance - android

I have two Fragments and two ViewModels with a similar implementation. So, I decided to apply inheritance. So I decided to create a FragmentParent and a ViewModelParent, and then ended up with:
FragmentA and FragmentB both inherit from FragmentParent.
ViewModel(A) and ViewModelB both inherit from ViewModelParent.
On one side, both parents will be abstract because they have methods to be implemented in different ways. On the other side, both children ViewModels have the common parent's viewmodel methods and also their personal custom methods.
So, viewmodel object has to call some common methods from the FragmentParent but, also, the problem is that each Fragment will call their correspondent viewmodels' custom methods. So, if I declare the viewModel object in the FragmentParent, once I use it in the children to call the custom methos of each correspondent viewmodel, it says error because the viewModel object's type corresponds to the ViewModelParent.
As you can see in the image, the methods in colour cannot be called because vM is instance of ViewModelParent and they belong to the custom ViewModels.
A solution could be casting the viewmodel object in each child fragment once I need to call the custom methods, BUT, I guess this is dirty. Any good idea for this approach? Thanks.

Casting the parent ViewModel to the specific child ViewModel seems to be the best option. It's a common use of casting, I don't see any problem with it. If you're accessing methods of the child ViewModel multiple times inside the child Fragments, you can store the casted ViewModel in a property or variable in each child Fragment. For example, if you're using Kotlin you could do something like this:
//FragmentA
val viewModel = vM as ViewModelA
viewModel.customAmethod()
//FragmentB
val viewModel = vM as ViewModelB
viewModel.customBmethod()

Related

Why is viewModel() used in a composabe and viewModels() in an activity or fragment?

In this link it is instructed to use viewModel() in any composable and in an activity, we will get the same object while calling viewModel(). Although it is instructed to use viewModel() inside a composable, I was able to use it in setContent{} (outside of any composable) also.
In this link it is instructed to use viewModels() in an activity or a fragment to get the object of a class that extends ViewModel.
In both of the cases, we are getting an object of a class that extends ViewModel. So, why do we need to use two different approaches (viewModel() and viewModels())?
If you ask if you can use only viewModel without viewModels in Compose, the answer is yes. But in some cases it is more convenient to use both of them.
viewModels belongs to the androidx.activity package, is an extension to ComponentActivity, and has nothing to do with Compose. It was used in view-based Android and can still be used with Compose when you need to initialize or update your view model with some activity-specific callbacks.
In turn, viewModel is part of Compose and allows you to easily create/access a view model from any Composable.
You can call it directly inside setContent since it already belongs to the composable scope, but you would not be as comfortable calling it anywhere else in the activity, such as in onActivityResult(I know it's deprecated, it's just an example). You can still do it as shown in this answer, but in some cases viewModels may be easier to use.

How to inject hilt viewModel to be used with fresh state in multiple navgraphs

The Problem:
I have a parent fragment that creates 1-4 splites of a single navgraph (split screen of a tablet). I have a global ViewModel, which I want to be able to only be shared in one of the pieces. so each split have a global viewModel
If I use #InstallIn(ActivityRetainedComponent::class) together with
#Provides
#ActivityRetainedScoped
I get one ViewModel shared between the pieces. See picture
If I remove #ActivityRetainedScoped Every fragment gets its own ViewModel. See picture
What I want:
Each piece creates its own parent ViewModel. See picture with green activity
I’ve only figured out 2 different ugly solutions. that might work:
Create the ViewModels in the parent fragment. and pass it in some way.
When talking to the global parent pass the index of the piece
Ps. Not in the picture. Connected to each fragment there is a fragmentViewModel. and those fragments talks to the global ViewModel
I was able to solve this by using a nested Graph.
private val viewModel: NameOfYourViewModel by navGraphViewModels(R.id.nameOfYourFragment)

Data Binding: Pass LiveData as is to BindingAdapter and observe it using a LifecycleOwner

I want to pass a LiveData as is to BindingAdapter.
Inside the BindingAdapter I want to do a Transformations.map and display different options the user can select and when clicked send the result back using the same LiveData.
In order to observe the LiveData in the BindingAdapter I need access to the LifecycleOwner, ideally of the fragment view. I need that if I want to call .observe on the liveData or set the LifecycleOwner on the new binding I create inside the BindingAdapter.
Any idea how I can do that?
Firstly, I'd like to recommend you not to go down the road of placing too much of your business logic into your BindingAdapters. Besides it being a good practice to use binders to simply set style attributes, I have personally seen spaghetti-code disasters made by placing too much logic into the adapters. This is a really sketchy practice as the code is run for every element that listens to your binding, every time your livedata changes so your logic can get pretty hectic, very quickly and your app performance can be decreased quite swiftly, too.
Having said that, I don't think you should be passing in LiveData into your binding but instead the Object E that is being held in your livedata. This way you can:
keep your work inside the fragment that both has a LifecycleOwner and is the recommended way to observe for changes
pass into your LiveData instance the result of your Transformation and
display it in your UI by receiving it directly through your binding
This way your adapter has only the logic to display the result/results and all the work is being handled correctly by the fragment.
ViewModel Implementation
If you'd like to take this a step further, following Google's recommended Architecture Components, I'd suggest you to place the logic inside your ViewModel (should you be following the MVVM pattern) and avoid the use of the fragment altogether. You'd place the LiveData variable inside your viewModel (say var itemColor: LiveData<Int> = MutableLiveData<Int>(R.color.colorPrimary)) , connect it to your binding through xml
i.e.
app:showColor="#{viewModel.itemColor}"
and place all the logic of your Transformations inside a function in the viewModel. Setting the value to itemColor would directly send the value to your bindingadapter (showColor) and you could use the value as needed without even touching the fragment or observing the variable!
Note: please remember to set the lifecycleOwner to your binding inside the fragment as so : binding.lifecycleOwner = this, otherwise the adapter will not listen for changes.
I hope this helped, Panos.

ViewModel in architect component - android

I have a lot of fragment base architect component.
Is this true that I create ViewModel for per fragment? or I should create one ViewModel for all fragment?
Saw many project that uses ViewModel for each Activity, and they pass them to their fragments if needed.
Same goes for me, but figure out what functions that Activity will do, and then build a ViewModel based on that functions:
Activity/Fragments that create an object.
Activity/Fragment that fetch a list.
Activity/Fragment that deal with Objects, like delete, update.
You can pass ViewModel to fragments. Also you can use inheritance with your ViewModels.

Does calling a ViewModel instance reset the LiveData?

In Kotlin I'm using
viewModel = ViewModelProviders.of(this).get(HomeViewModel::class.java)
To retrieve a ViewModel from the providers.
Inside my ViewModel I have something like this.
val liveChuchuData = MutableLiveData<DataChuchu>()
From my understanding this creates a final new variable of MutableLiveData right?
I remember when declaring MutableLiveDatas in ViewModel in Java, we create a function and then check if the MutableLiveData is null to only create it once.
So what if I have a fragment that will also use the same ViewModel instance.
val liveChuchuData = MutableLiveData<DataChuchu>()
Will that line cause the current data to be reset, once called in a fragment?
Depends on what is the parent of your ViewModel. If parent is Acivity and in your Fragment you initialize your ViewModel with getActivity() instead of passing this, then you will reuse that ViewModel, but for example if you have two separate Fragments that initialize same ViewModel by passing this to ViewModelProvider then your ViewModel will have two separate instances and different data in them.
To have same data in ViewModel in two Fragments, you need to pass getActivity(); to ViewModelProvider when creating your ViewModel instance.
That said, YES, it will cause your data to be reset if you use this when creating ViewModel.
Hope this helps. Good luck :)

Categories

Resources