This docs provide a way to observe data stored in database. But this is too generic. This emits a new value whenever any changes occur to that table. Using this to update recyclerView means, on every update I have to replace old dataset by new one and call notifyDatasetChanged on the adapter. But instead is there any to specifically get updates like onRowInserted, onRowUpdated and onRowDeleted which returns only the data in the row being modified, as this helps me in making specific notifyItemInserted, notifyItemChanged and notifyItemRemoved calls to the adapter.
Related
I am doing a side project of making an app (with Java since I already know it). I have a recyclerview which loads some data via the room database library. The elements of the recyclerview are clickable.
My problem is I want the user to be able to sort the recyclerview so that the most recently accessed items go to the top.
My original idea was to assign the entities to have two variables - a String list_name which also serves as the id, and an Int order_of_access. Also, in my ViewModel I have a getAllLists method which returns a livedata list. I have an onChanged listener in the fragment activity which nicely updates the recyclerview when data is added/removed.
When the user adds a new list, it is assigned an order_of_access of the listsize (+1). But when the user deletes a group of lists, or clicks on a list, I want to update the order_of_access, say with an updateOrderAccess method.
Do you think this is the best way of doing what I want?
Where should I place updateOrderAccess and how would you recommend it be written? Since the method getAllLists returns livedata, it is tempting to put updateOrderAccess in an observer in the fragment (in onChanged) - but this will obviously create an infinite loop. It seems more in the correct philosophy to put it in the ViewModel, but then how would you suggest the updateOrderAccess method to be written? I'm having some trouble conceptualising what I need.
I hope the question is not too vague - I will update it if you need more details.
Where should I place updateOrderAccess and how would you recommend it
be written?
I am so sure that you must write it in the view model, as long as updateOrderAccess() is editing the list which is observable then you have andexpose by that the ui state then you have to put it in view model, and the observers will be notified ( in this case it is recycle view) and it will redraw the list in the order you offered.
note: do not you ever update the state(ui data) outside the state holder so you implement UDF (unidirectional Data Flow) pattern.
see the references below to read more about UDF so you never get confused where to declare your functions by letting the architicture lead you:
Guide to app architecture
ui layer
state holders and ui state
Do you think this is the best way of doing what I want?
i am not very sure that i got exactly what your app do, but it seems like you want to re-order the elements of recycle view depending on the ui event (click) or data change (deleting or adding new element), now you have two choices:
if the order is very importnat to you that much you want to keep it even if the app has been destroyed
then you have to add a field in the room entity represent the ordering (let us call it order) and whenever the user click on the recycle view you have to update the rooms field "order" which is "flow" or "liveData" or any observable type, that will tell the view model that there is a changing in the data, now the view model have to re-order the new data by the field "order" and pass it to the recycle view to show it.
if your app do not have to save the order changes after the app been destroyed
then you can simply do that:
create list which is called "orderedList" you will put the list items in it by the right order, and another list called "unorderlist" which have getAllLists
for the first case where the ordering is being changed by user click, you
can declare a function in viewModel then use it in the ui
controller (your activity or fragment), so whenever the list item is
clicked this function just re-order the orderedList elements ( which
is observable, so the changes reflect on the ui ) just by change the
clicked item position to the front of the list.
for the second case where the ordering changes by data changes like
add or delet a list item in the database, then you have to compare
the legnth of orderlist and unorderlist legnth, if unorderList is
longer then it is an add situation else it is a delete situation, in
adding case just add the last item of unorderList to the orderList,
else you have to check the deleted item and delete it from
orderList.
I am using a room database to populate a RecyclerView. In the settings, the user can back up and restore the underlying sqlite database. Backup/restore is a simply copy mechanism like here.
I restore the database back into the location getDatabasePath(MyDatabase.DB_NAME), which works totally fine. However, if the user leaves the Settings Activity, the RecyclerView is not refreshed.
I guess that I have to tell Room that the DB has changed. But how do I do this? Thanks!
It is simple, you can refresh the recyclerview with this form
Put on resumen state
public override fun onResum() {
super.onResum()
list.remove(position);
recycler.removeViewAt(position);
mAdapter.notifyItemRemoved(position);
mAdapter.notifyItemRangeChanged(position, list.size());
}
States
You can return a LiveData list from the Dao, say LiveData<List<MyPoJo>>, and observe it it in your activity; inside this observe() notifyDataSetChanged() for your RecyclerView.
This will convey live change of data whenever the Room changes to the underlying list of the LiveData
You can also use DiffUtil if you've a big list of data so that when the notifyDataSetChanged()gets called, only the changes will take place in yourRecyclerView` adapter.
Here's an example of using DiffUtil
And here is an answer that with sample code
I want to populate RecyclerView using database. As currently there is no inbuilt adapter for populating RecyclerView using database, I have used CursorRecyclerAdapter by Shywim. I have created a sample App to test it and it worked fine. The feature I didn't liked is having an _id column in the resultset and calling swapCursor() on each database operation, mostly insert and delete. This goes same with ListView when using SimpleCursorAdapter. My query is what if I use ArrayList as the dataset instead of directly using the Cursor.
Benefits of doing this(my assumption) :
No more a need of _id column in the resultset.
Can fetch the data from database, put it into ArrayList and close the cursor.
No need of calling swapCursor() on each database operation as I can add/remove specify elements from the ArrayList and call notifyDataSetChanged()
I don't know the exact logic behind swapCursor() and notifyDataSetChanged(). So, can't decide which one is light-weight and efficient.
If someone has experienced this or done this before, please clear my doubts. Any corrections and suggestions are most welcome.
Using array list and custom adapter is better way for this as per my understanding.
See some scenarios below :
1) Cursor will close after each transaction so database will work smoothly.
2) As you can close cursor on operation done so it will never generate cursor not closed exception.
3) You can modify view of each row easily and manage custom adapter as per your choice.
There are many other reasons, but in short custom adapter is better then cursor adapter as per my understanding.
Say I have a List<User>. Now I can wrap this list in an ArrayAdapter.
List<User> users = Users.getAll();
ArrayAdapter<User> = new ArrayAdapter<User>(this, android.R.layout.simple_list_item_1, users);
I then bind the adapter to a listview to display the list of Users.
Users.getAll() uses Sugar ORM to query the database and return a list of users. Items can be added to the user list from the activity that displays the user list. I am wondering how do I keep the listview updated.
Option 1
One way is to manually update the users as a I add to the database and then call adapter.notifyDataSetChanged(). This works, but it doesn't feel right because I am maintaining a "fake" list that represents what is in the database.
Option 2
I am wondering how bad is it if I just clear the items in users, update it with the results of a new database query and then call adapter.notifyDataSetChanged()?
Will all the child views be thrown away and be re-rendered? Or does it call the equals() method to see if the models bound to each child is the same and then update only what is new?
Other Info
Since I am using SugarORM, I don't think I can get access to the Cursor to do something more efficient. However if there is a better way to keep the list synced with SugarORM, I am happy to hear that as well.
In answer to your option 2: No, it doesnt call equals, because the adapter works in conjunction with the widget to re-use the views, it doens't create a new view foreach item in the list, it create a view foreach visible item and as you scroll re-uses view that left the screen.
The best option here is to create your own adapter, creating a class extending BaseAdapter and creating your own logic inside it requerying the database and notifying the change to the listview (or gridview)..
On the other hand doing what you said here:
I am wondering how bad is it if I just clear the items in users, update it with the results of a new database query and then call adapter.notifyDataSetChanged()?
isn't bad either.
Create a DAO class that extends Observable, then have your Adapter implement Observer. Now every time you add or remove a SugarRecord, do through the DAO class and whoever is register as the Observer will get notified through the following method:
#Override
public void update(Observable observable, Object o)
You can more about Observable/Observer pattern here. This is just one of the many examples and tutorials out there.
sorry for stupid question. But really interesting and incomprehensible. In this session discussed about notifyDataSetChanged() method.
From documentation for this method - "called when the data set being observed has changed, and which when read contains the new state of the data". My English bad and I do not understand all. But I right if guess that method called when I need refresh ListView with new data set?
If I'm right then I'm confused. In the past and my first program I played with contacts api of android. And run some processing in an asynctask. At this time appeared dialog with progress bar and in the background, you could see how the state of ListView changed in real time. Data for ListView row changed via BindView.
Why? So I'm in something wrong. Explain please.
As i read it, BindView is only used with cursors, which are a specific type of a data set basically. You can have alternative data sets, there is for example an ArrayListAdapter in the API which uses an ArrayList as its dataset. In case that data set changes, notifyDataSetChanged() will have to be called to notify the list view that its bounds will have to be recalculated and its views have to be redrawn (and probably some more).
If you decide to write your own and create the possibility to modify the data shown in the list view through an adapter (one could imagine adding method like addObject(SomeObject o) in your home made adapter for example), then you'd call notifyDataSetChanged() in that method.
Similarly if you have a deleteObject(SomeObject x), if the remaining data set is larger than zero you'd call notifyDataSetChanged() or when the remaining data set is empty you'd call notifyDataSetInvalidated() which in turn will to some extra stuff like setting the so called empty view in the list if you have one specified.