My question is kind of not strictly precised. I have a Fragment with list of items that are backed up by REST API service:
GET /api/items
I have a ItemsViewModel class, ItemsProvider with LiveData objects and APIService that get the items from REST service. The implementation of ListFragment observes the ItemsViewModel and its state.
Now I have a question. How do you implement inserting data into ViewModel in your implementations? How do you handle state update?
You can take LiveData<List<? of items>> as MutableLiveData inside ItemsViewModel.
Now, when you've new items, you need to set your LiveData value.
You need to create an observer to that inside of your activity/fragment where you needed.
Related
I have multiple Activities with the same CardView. To inflate this CardView I reuse the ViewHolder in my adapter. Now I'm trying to handle a click on the ViewHolder with setOnClickListener() and I need to save the Model represented by CardView into the database using a Repository. So for MVVM, the repository can communicate only with ViewModels and Databases or other resources, but every Activity has a different ViewModel so I couldn't pass the ViewModel to the Adapter to update the Model into the Database.
So I'm thinking that the adapter that I reuse, needs only one ViewModel for the database actions indifferently from the Activity that uses it.
So I need a static method in the main ViewModel that saves that Model.
This approach is correct for the MVVM pattern?
I don't know if I understand your question correctly but I guess that you want to somehow send a data from recyclerview adapter to your database.
"so I couldn't pass the ViewModel to the Adapter to update the Model into the Database"
You should never pass any viewModel to the adapter class because you will tightly couple them together and they wouldn't be reusable anymore.
In your case you should send data from adapter class and receive it in activity. Then activity should call viewModel to update specific data and viewModel will then call repository which will access database and save data.
It sounds complicated but it is well organised and you can reuse all components in many activities/fragments because they are not depending on each other. Adapter sends events that are received by your activity but the adapter does not know (and shouldn't know) which activity will receive the data. If you would pass viewModel as a parameter to the adapter class then it couldn't be used in activities that have different viewModel.
P.S Try to not use activity for all your views, in Android we have a Fragment component which is very useful in that case.
Useful links
fragments https://developer.android.com/guide/fragments
MVVM https://proandroiddev.com/understanding-mvvm-pattern-for-android-in-2021-98b155b37b54
I'm making a function that opens a dialog when I touch the recyclerView item. I put viewModel in adapter and onClick function in viewHolder to open dialog with viewModel.
It looks like this.
Adapter(viewModel) // Initialize adapter in Activity or ViewModel
↓
class Adapter(viewModel: ViewModel) : ViewModel() {
inner class ViewHolder() {
fun onClick(binding: RecyclerViewItemBinding) : RecyclerView.ViewHolder(binding.root) {
Dialog(viewModel) // Open Dialog with viewModel
}
}
}
Can I use ViewModel in adapter or Dialog? If can't, what should I do? I need to change the data of the ViewModel by receiving the changed value from the Dialog.
ViewModels should be limited to activities/fragments, avoid passing it around to whereever you like it. Use liveData to pass events around, for example to pass click events interceptable in the adapter to the activity/fragment to which the viewModel is attached to. Also use liveData to notify your adapter about data changes that occur inside the viewModel. I have created a simple project that reflects your desired outcome, please have a look:
https://github.com/phamtdat/ViewModelForAdapterDemo
key points:
data are handled in the viewModel only
notify the data changes using liveData
update adapter on data changes
intercept click events in viewHolder, and forward it to adapter
forward click events from adapter to activity that the viewModel is attached to
show dialog with corresponding data on click event
change the data in dialog logic
the result should be: updated recyclerView displaying new data
This way you have clear separation of concerns:
viewModel - handles only data
activity - handles only UI events (in our case the click events of items and showing the dialog)
adapter - handles only correct rendering of item UIs
For simplicity, I didn't use dataBinding, but of course I would if I had time, that way you don't have to update the UI inside the viewHolder, but just pass the model to the binding.
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.
This is the 'like' feature on Facebook.
I would like to synchronize these recyclerviews with these two pieces.
If you click on the 'Like' button on the recyclerview in one piece, the 'Like' button on the recyclerview should change when you change to another piece.
Which method should I use?
interface?
service?
Map Should I use this?
What method do you use to synchronize the data of two fragments?
You should be using ViewModel's from architecture components.
https://developer.android.com/topic/libraries/architecture/.
Basically you create a view model in the activity so that it is stored with the activity scope
//this is the instance of the activity
ViewModelProviders.of(this)
You can then get an instance of this view model in each fragment using
ViewModelProviders.of(getActivity())
The view model can then be used like in a standard MVVM architecture.
Each fragment should register to the lifecycle aware components that the ViewModel would provide. MutableLiveData is one such component that you could use to provide the data back to whoever is interested in the data (in this case each fragment)
Be aware that LiveData while does a fantastic job can be limited as it stores data as a state in time. This is great, but android should be developed where it is driven by events)
As an example If you have a viewmodel which sends data to the view via livedata it could trigger a dialog. When the user closes that dialog and causes a configuration change (destroys and recreates the activity) the view will receive the state of the live data at the point in time it was set which will again show the dialog. Basically each time you rotate the device it could show the dialog agian even though you've dismissed it.
A hacky fix to this is to notify the viewmodel to remove the state in the livedata after the dialog is dismissed. but this creates a number of other issues including tying view state with the viewmodel
It's a lot more flexible if the Lifecycle aware component instead sends events of when data changes. Think Rxjava that is lifecycle aware. You add the data to the RXJava component and the observable provides the data to the observer when the view is in a state to consume it (> onresume and < ondestory).
Hopefully that gives you a starting point. Let me know if you need more details
I'm creating a sample app (Last-Mvvm) to learn (and possibly show) the usage of mvvm pattern, using Android data binding.
I have an activity with my ViewModel object inside. I have also a RecyclerView adapter, which contains an arraylist of items that are converted to another Viewmodel. I want to save the state of the list inside the adapter (for rotation changes).
So: where should I save it? inside the activity? Or in the viewModel of the activity? Or somewhere else?
Also, there is another thing that isn't really clear.
Is it fine to perform rest calls (through Retrofit) or database calls directly from the viewModel (since i'd use interfaces), or would it be better to make an interface that the view (activity) implements and performs all the calls?
I will try to explain
First. So: where should I save it? inside the activity? Or in the viewModel of the activity? Or somewhere else?
and
Second. Is it fine to perform rest calls (through Retrofit) or database calls directly from the viewModel (since i'd use interfaces), or would it be better to make an interface that the view (activity) implements and performs all the calls?
on example from article.
On the schema below you can see implementation of OnClickListener and OnLongClickListener for RecyclerView item.
doted lines is a links
solid lines is a method calls
How it works
ViewHolderWrapper works as ClickListener for root view of ViewHolder. Depending on implementation ViewHolderWrapper can be proxy for HolderClickObservable or depending on SelectionHelper, ViewHolderWrapper can manually highlight selected item.
SelectionHelper is responsible for saving selection state and notify SelectionObserver about changes.
Listener (Adapter in this case) is responsible for highlighting of selected items and for updates.
Summarizing
First. You can restore adapter state via activity with methods onSaveInstanceState() and onRestoreInstanceState().
Second. You need to create lightweight ViewHolder which only responsible for binding data to view. Actions can be performed at adapter or activity.
See also example application