I have a class called Person one of the variables is a realm list called clothes:
public class Person extends RealmObject{
private RealmList<Clothing> clothes;
}
Clothes is used to display in a RecyclerView wherein the contents can be edited such as quantity, color of clothing, or cloth type.
When I edit the clothing values:
public void setCloth(int i, Cloth cloth) {
realmInstance.beginTransaction();
clothes.set(i, cloth);
realmInstance.commitTransaction();
}
Of course, the record is saved. My problem is what if the user, cancels the editing of the WHOLE Person object. Then the clothes list change will persist.
Please help.
I'm sure you would be a great fan of nested transactions (https://github.com/realm/realm-java/issues/1509).
In the next release of Realm, a new method copyFromRealm() will be introduced. You should copy the clothes list and use copyToRealmOrUpdate() when you commit the Person object.
I have now solved the issue:
The solution is if you're using a fragment or an activity, and displaying the realm object and it's relationship through widgets, begin transaction immediately.
Realm realmInstance = Realm.getInstance(this);
realmInstance.beginTransaction();
This way, changes done directly on the realm object whether a relationship or a plain variable will be allowed.
To cancel the changes done simply use:
realmInstance.cancelTransaction();
This code can be invoked by overriding onBackPressed or any means of canceling the editing.
After canceling the transaction, any changes done including the RealmList objects will be forfeited.
More Info:
I was displaying the clothes list array in a recyleview through an adapter with editing controls (etc. setting the qty, changing cloth type). This solution can also be used in that scenario.
Hope this helps.
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'm working on an app consists of
three Buttons to enter three different Fragments, each of them contains a List. when you press on the toggle Button next to any ListItem it is added to a SQLite database.
Button for Summary contains three lists to populate saved data from each Fragment.
My problem is (for example how can I retrieve saved data from fragment_one ONLY to the listView_one in the Summary activity) ?
N.B. all the data are of the same type (from the same model) and the saved ones got saved in the same database.
I've searched and found that I should use Fragment tag during FragmentTransaction..
What should I do after I set a tag for each Fragment?
When should I check the tag to populate the data correctly?
Sorry for that long question but i'm little overwhelmed and got stuck at this point.
If I understand correctly your problem, we have 2 ways to do this:
add a tag column in your db, then you can retrieve saved value from each fragment with your desired tag.
create a singleton manage each fragment saved data, etc:
public class DataManager {
// singleton implementation
...
private List mFragmentOneSavedData; // just save db row id to query later
...
}
Thanks.
This question is about Couchbase lite (no Sync Gateway).
I'm new to Couchbase, I managed to use the demo app, but I don't understand it completely.
It contains this code which (as far as I understand, since I'm not native English speaker) retrieve views to populate a listview with the indexes:
// This code can be found in ListsActivity.java
// in the setupViewAndQuery() method
com.couchbase.lite.View listsView = mDatabase.getView("list/listsByName");
if (listsView.getMap() == null) {
listsView.setMap(new Mapper() {
#Override
public void map(Map<String, Object> document, Emitter emitter) {
String type = (String) document.get("type");
if ("task-list".equals(type)) {
emitter.emit(document.get("name"), null);
}
}
}, "1.0");
}
listsLiveQuery = listsView.createQuery().toLiveQuery();
Could anyone give me a hand with what each part is doing?
In which step is the listview populated
Can I change "list/listsByName" in the code (line 3)? What would happen?
Can I emit more than one element?
The code is a little bit convoluted. Let's answer the easy parts first.
Can I change "list/listsByName" in the code (line 3)?
Yes. That's just the name of the Couchbase View. You choose the View name. Unfortunately the terms used in Couchbase and Android overlap some. A Couchbase View is a kind of static index of your database.
Can I emit more than one element?
Yes. You can emit most anything you want. Take a look at the documentation here
Now, tracing how the Android ListView gets updated:
In ListsActivity.java notice in the onCreate method a ListAdapter instance gets added to the ListView. This ListAdapter is a private inner class that extends LiveQueryAdapter.
LiveQueryAdapter is in the utils subpackage. If you look at its constructor, you'll see it adds a change listener to the query passed in. When triggered, this change listener sets an enumerator equal to the rows passed back by the live query, then calls notifyDataSetChanged to tell the list to refresh itself. That, in turn, causes getView in ListAdapter to get called. That's where the data is pulled from the database and used to populate a list entry.
I'm managing my UI list with RealmRecyclerViewAdapter. I'd like to add a "pending" item to the collection that will display immediately while simultaneously posting the new item to the server. In case of failure, I will remove it from the display. Similar to a chat feature; I display the chat when they hit send, and if for some reason the post fails I can remove it and alert the user.
The problem is that with a regular RecyclerView adapter, I can just add one item to the end of the collection without an id, and remove it as needed. But since RealmRecyclerViewAdapter uses managed RealmResults, I can't add a new item without an id.
Can someone suggest a way to handle the situation I'm describing with RealmRecyclerViewAdapter?
Without knowing what your data model looks like. It is hard to give concrete advice, but it sounds like all your have ID's, so something like this should work:
1) It must be possible to generate ID's offline. The standard solution for that is GUIDs: https://en.wikipedia.org/wiki/Universally_unique_identifier
2) Have a boolean on your model class: "syncedWithServer" or something similar. Then you can render all items not yet synced as "pending".
public class ChatEntry extends RealmObject {
#PrimaryKey
private String id = UUID.randomUUID().toString();
private String entry;
private boolean syncedWithServer;
}
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.