how to refresh data in a fragment after changes by another activity - android

I am implementing a small app where I have a list of items (1).
With a fab button I start another activity that is able to add a new item (2).
In (1) I have a ViewModel loading data from a repository.
In (2) I store the new item on the repository.
When the user save the data in (2), that activity is closed (calling finish()), but "of course" in (1) I don't see any change.
I read around that I should call notifyDataSetChanged() on ViewModel used in (1), but I do not understand where: in (1) in method onResume()? or in another place?
Can you help me? Maybe e simple tutorial explaining this specific aspect?

If you are using just a repository without any modern storage like Room or Datastore and just save the data in memory(Repository) then you should make your repository Singleton and save items in LiveData OR Flows inside repository then observe it.

Related

How to use Repository data in two different fragments

I have FragmentA and FragmentAViewModel. FragmentAViewModel calls RepositoryA for getContactData(), which is a silent call without blocking the UI.
Now User Navigate to FragmentB which has FragmentBViewModel. User needs the getContactData() response here in FragmentB.
I want to show loading progress in FragmentB until the data is available in RepositoryA. Once Data is avaiable in RepositoryA, i want to update the FragmentBViewModel and FragmentB .
How can do that.
NOTE:
I dont want to share the ViewModel since this is the small use case in whole feature.
I cannot use LiveData in Repository since i need Lifecycle owner to observe that.
Do i need to use RXAndroid or Broadcast for this ?
The flow is a bit strange to me but it's totally doable.
If both pages are reference to the same data source, ex: same database via Room interface.
Then first page can trigger the fetch or update while the second page subscribe for the update and display it.

How to refresh fragment using Navigation component

I'm practice in using Navigation component. Now i have a trouble. I'm using Room database and i'm update it in my Fragment multiple times. Somehow it's anyway taking my last result no matter that i'm updating it so i decided that i need to update my fragment but i don't know how. I'm already found solutions like to detach and then attach fragment but i don't know how to do it because in my MainActivity i'm attaching to my fragment through "setupActionBarWithNavController".
I think you are overcomplicating matters. If you are using Room to manage your data, and the values in the database are getting updated by your code, then you can simply use LiveData. You don't need to reload your fragment at all.
Have your Room DAO query return a LiveData<MyType> instead of just a MyType object (for example). You can then "observe" the LiveData in your Fragment, and write a callback function to update your UI every time the value returned by the query changes. See this page for more info.
This basically means that when you tell your model to updateInsulin, once the database value is changed, the LiveData watching your insulin level will change and trigger your Observer. The Observer can then do something like change the text of a TextView or naviagte to a different Fragment.

Reset/clear viewmodel or livedata

I am following the one-single-activity app pattern advised by Google, so if I want to share data between Fragments I have to share a ViewModel whose owner must be the parent Activity. So, the problem becomes because I want to share data between only two Fragments, independently from the others.
Imagine I have MainFragment, CreateItemFragment and ScanDetailFragment. So, from first one I navigate to CreateItemFragment in which whenever I press a button I navigate to ScanDetailFragment in order to scan a barcode and, in consequence, through a LiveData object inside the ViewModel I can get the scanned value back into the CreateItemFragment once ScandDetailFragment finishes. The problem becomes when I decide to cancel the creation of the item: I go back to the `MainFragment' and because the ViewModel's owner was the Activity's lifecycle, once I go again into CreateItemFragment, the previously scanned value is still there.
Any idea to reset that ViewModel?
but, aren't Viewmodels also aimed to share data between different views?
No. Each viewmodel should be responsible for one view. The "shared viewmodel" pattern is for cases when you have one large view (i.e., activity) that has multiple subviews (i.e., fragments) that need to share data / state, like the master / detail example in the documentation. It's a convenience for these cases when you need real-time updates amongst the subviews.
In your case, you're navigating between fragments and as such should be passing data through the transitions. This means passing arguments along when starting new fragments and registering for results when they complete their task.
Then each of your fragments is isolated, self-contained, more easily testable and you won't end up with a God-ViewModel that does All The Thingsā„¢ and turns into a giant mess as you try to jump through hoops accounting for every state it could possibly be in.
You can use callbacks in such cases to share data between fragments. or if you use DB/Sharedpreference/Content provider then you do not have to worry about sharing data each page will fetch its own data from the store(DB/SharedPreference/Contentprovider).
you can also try https://medium.com/#lucasnrb/advanced-viewmodels-part-iii-share-a-viewmodel-between-fragments-59c014a3646 if this guide helps
You can clear LiveData value every time when you go into CreateItemFragment from MainFragment.
Or you can just clear it from the CreateItemFragment in onBackPressed() method.
When you cancel the creation of item,set livedata value to null.then within observer code if(updatedvalue!=null) write your code using updated live data value.in this way you can avoid last updated value.
At the moment (on 2022), the method viewmodel.getViewModelStore.clear(); or onCleared(); is deprecated.
So, if you want to clear data holded by ViewModel or clear value of LiveData, you just need use 1 line code like this:
mainViewModel.getLiveData().getValue().clear();
getLiveData() is my method inside MainViewModel class to return liveData variable
getValue() is defaut method provided by LiveData (MutableLiveData: setValue(), postValue())
If you need to clear data when user press on Back button in Fragment, you can do like the code below & put it inside the onViewCreated method - the method of LifecycleFragment.
private void handleOnBackPressed() {
requireActivity().getOnBackPressedDispatcher().addCallback(new OnBackPressedCallback(true) {
#Override
public void handleOnBackPressed() {
Objects.requireNonNull(mainViewModel.getLiveData().getValue()).clear();
requireActivity().finish();
}
});
}
My project on Git if you want to refer code (it still updated): https://github.com/Nghien-Nghien/PokeAPI-Java/blob/master/app/src/main/java/com/example/pokemonapi/fragment/MainFragment.java
I disagree with #dominicoder. At this link, you can find a Codelab made by the Google team updated to Oct 30, 2021. The shared ViewModel pattern can be used when you need a coherent flow to achieve a specific task inside your app.
This method is useful and a good practice because:
The Jetpack team says that has never been a recommended pattern to pass Parcelables. That's because we want to have a single source of truth.
Multiple activities have been heavily discouraged for several years by now (to see more). So even though you're not using Jetpack compose, you still should use a shared ViewModel along with fragments to keep a single source of truth.
Downside:
You need to reset all the data manually. Forgetting to do so will bring bugs into your app, and most of the time, they're difficult to spot.

Saving data to sqlite database and updating recycler list adapter

I'm a newbie to android app dev and I have one little problem. I'm developing a note app with a single activity(MainActivity) which has a NavHost and host three fragments(NewNoteFragment, NoteListFragment and NoteUpdateFragment). The note details are stored in a sqlite database.
Whenever user creates new note or update the existing note, the changes can only be stored to the database in NewNoteFragment or NoteUpdateFragment's onPause override i.e when the user navigates from the said fragments to the NoteListFragment. Although the changes were saved but the recyclerView in the NoteListFragment doesn't update immediately until i kinda navigate away from the NoteListFragment to other fragment.
When I check the logcat i noticed the onResume override(which is where I called my adapter.notifydatasetchanged() method) in my NoteListFragment is called before onPause override of the replaced fragments.
Is there a way to change the fragments navigating behaviour where onpause is called before the onresume? Or any better way to achieve my said aim.
You cannot set onPause to be called before. Since you are using fragments try to update the recyclerView onViewCreated() function of Fragment.
You cannot change the Android Lifecycle, it is always set.
I would advise using Room and LiveData, any changes in the database would automatically update the RecyclerView you mentioned.
You can read more about live data here

Android ViewModel inside RecyclerView Adapter for lazy database downloads

I have a question that is more related to proper design and architecture of MVVM than to coding itself. In my project I have a situation that on ViewModel is suplying data, that are later used in RecyclerView.Adapter to create a proper view.
However I wonder if this would be also correct (from proper 'way of doing things' POV) if some of the data would be supplied in form of id's to be further fetched from Room or external server? For instance during onBindViewHolder use some LiveData with observe() to update certain fields on succesfull load.
Doing data fetch in the views is a no-go. It defeats the very purpose of MVVM and in particular the Android Jetpack efforts. One of the big reason is the data needs to survive configurations. Putting "data fetching" mechanism in the view defeats that as the view can be destroyed and recreated anytime when need be.
So I would suggest you make sure all calls to the network or any other source of data for that matter revolve around the ViewModel and never a view. Let the VM feed the data to the View through observer.
Exception to what I have just said is such use case as loading images with Picasso or Glide where by you feed them URL and they load image. But that's a different thing as they are designed to handle that.
UPDATE with followup Questions
it's ok to put observe() still inside Adapter, during the binding process?
No! Data sent to the adapter must be complete in the purpose it is supposed to serve. For example, if you have to do list app and your Top-Level Activity displays all Todos, then you must feed adapter will complete data (Title, Created time, et al).
The data that are not going to be displayed (like descriptions or sub-to-do-lists) and aren't necessary to identify specific to do should not be fetched (unless you want to store them for sole purpose of avoiding a second network call and pass them to the next activity as serialized data).
When user clicks specific To-Do, then launch new activity with its own ViewModel that will fetch details for that activity (assuming you passed some ID with intent).
If the first, then I understand that observe() should not only update data, but also populate it later to Observer and call notifyDataSetChanged(), right?
Observe is a way to post data to the view when either it have changed or the old view was destroyed and so the new view is being given the same old data that survived the "view death". So it is in the same method where updating data of the Adapter should be done and hence call to notifyDataSetChanged() or similar methods.
I hope that makes it clear.
I think it's best to keep the ViewModel separate from the Adapter. From what I'm gathering you want to basically have each list item load it's own data from Room by having each list item observe on a particular ID for that item. This sounds rather inefficient as you're basically having each item execute a query on the database (or network call) to retrieve just one item for all items that are going to be displayed, think about how it will scale if you were displaying a 100 items. I think it's best that you just execute one query to get the list of data and set the data to the list items in the adapter, keep it simple. Note that onBindViewHolder() doesn't just get called once but multiple times when you're scrolling the screen, so it could get quite ugly if you're trying to lazily load every list item.

Categories

Resources