How to manage full change of a recycler-adapter in android - android

I'm trying to figure out how to manage an adapter and the corresponding recyclerview appropriately.
The situation is the following:
I'm doing search queries on a database, and each time a new search is entered the whole dataset may change. Should I instantiate a new adapter each time? Or should I just remove items (even all items) from the adapter and insert new ones? Or something else?
EDIT: I should explain the situation better.
I'm using Algolia to store some data, which is retrieved in JSON objects.
Then the JSONArray obtained is passed as string to the adapter, which will parse each element in the string and the corresponding data of each element.
Since the searches are issued each time a new character is written (or removed), each time I obtain a new JSON response, it could be similar, the same, or completely different than the previous one.

You can do the following
mAdapter.notifyDataSetChanged();
or you can do ,
mAdapter.notifyItemRangeChanged(position, list.size());
Also, you can try the following to update recycler view
recyclerView.setAdapter(new RecyclerViewAdapter(newList));
recyclerView.invalidate();

just add new function for update the list
public void updateList(List<DataHolder> list){
displayedList = list;
notifyDataSetChanged();
}

Related

How to prevent double items but also prevent entire list from refreshing?

I am getting a JSON Array of user ids strings from a database:
["uid1", "uid103", "uid322"]
I am fetching this array on a time triggered loop, and I use notifyDataSetChanged with myDataSet.clear() to display the list, and to prevent duplicates. The problem with this method is that the list is constantly "refreshing", and I just want to either add or remove the items that's needed to be removed or added.
If I remove the clear() part, then the list won't refresh, but I will get duplicate items.
This is how the code (+pseudo) looks like:
trigger(just a time based loop) {
myDataset.clear();
jsonArray = //the id array from the database;
for (int i=0;i<jsonArray.length;i++){
myDataset.add(new User(jsonArray.get(i), imageUrl));
mAdapter.notifyDataSetChanged();
}
}
You can use for this purpose DiffUtil class (Medium example).
It calculates differences between new and old datasets and updates it.
But if you don't want to deep dive into DiffUtil you can try to use combination of notifyItemInserted() and notifyItemChanged()
You need to compare the 2 lists before refreshing. So,
If an item is removed, Your list's size will be smaller.
If an item is added, the list's size will be bigger and the new item will be the last item in the retrieved array.
But if an item is removed and at the same time an item is added, your list's size will be the same but the last item will change.
So, you need to check both array size and the last item before refreshing like this:
trigger(just a time based loop) {
if((myDataset.size() != jsonArray.size()) || myDataset[last] != jsonArray[last]) {
myDataset.clear();
jsonArray = //the id array from the database;
for (int i=0;i<jsonArray.length;i++){
myDataset.add(new User(jsonArray.get(i)));
mAdapter.notifyDataSetChanged();
}
}
}
Change your code as below, Your loop will do all the changes in your data and once the loop is over you notify those changes to your list in one go, instead of refreshing it each time.
for (int i=0;i<jsonArray.length;i++){
myDataset.add(new User(jsonArray.get(i)));
}
mAdapter.notifyDataSetChanged();
If you want to avoid duplicates use Set, add items to set from your json array and use it as datasource for your list.
You have a basic misunderstanding of the timer trigger. The loop will not pause there waiting for your next User entry from JSON. So adding notify in the loop is very bad practice. One notify outside the loop is enough. If a new User is added, it should appear in another timer trigger, i.e., another loop.
If you are seeing duplicate, that must be in the code handling the JSON to insert your database. However this code is not listed here so you need to look at that code.
Adding a unique index in the database table is a good solution.
for avoiding duplicate use contains
myDataset.clear();
for (int i=0;i<jsonArray.length;i++){
User user=new User(jsonArray.get(i), imageUrl);
//now check if its already in list or not by using contains.
if(!myDataset.contains(user)){
myDataset.add(user)
}
}//end of loop
and then pass list to your adapter and then call notifyDataSetChanged()
mAdapter.notifyDataSetChanged();

Couchbase lite on Android, retrieve views

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.

Adapter stuck to an old ArrayList

I am performing some reordering in a couple of array list, I have an adapter called
adapterMeasureEvi
which is set to a static ArrayList called measureEviArray from DataIpat class.
When debugging I can see that the static list is been assigned properly and it follows a notification to the adapter that the list has changed.
DataIpat.measureEviArray = (ArrayList<MeasureEvi>)measureEviArray.clone();
adapterMeasureEvi.notifyDataSetChanged();
Problem is, when getView() method gets called the first item it brings is from the old list, when I look up into the objects their indexes have changed that means I have updated the attributes but why is it still stuck on the old list?
/////EDIT////
I just noticed on the constructor of the adapter class that the list is definitely the old one.
public MeasureTableAdapter(Activity context, ArrayList<MeasureEvi> myMeasureEvi) {
super(context, R.layout.adapter_tablamedida_item, myMeasureEvi);
this.context = context;
this.myMeasureEvi = myMeasureEvi;
}
this constructor is called just once when the object is instantiated, so I suppose it means it will be stuck there, how can I update that list?
I think the problem is that when the data change, you recreate the DataIpat.measureEviArray instead of updating it. Hence your adapter will point to the old array, and the DataIpat.measureEviArray points to the newly upldated array. One way to fix your issue is instead of doing this (create a brand new array):
DataIpat.measureEviArray = (ArrayList<MeasureEvi>)measureEviArray.clone();
You should just update the DataIpad.measureEviArray array so that this array contains your new data (e.g. using clear and addAll to basically get the same effect as creating a new ArrayList).
Just stop cloning the list and work over the original worked, weird.
Deleted this,
DataIpat.measureEviArray = (ArrayList<MeasureEvi>)measureEviArray.clone();
not ideal but a workaround

Getting all of the items from an ArrayAdapter

I have a ListFragment backed by an ArrayAdapter that gets populated by a Loader. When the user clicks on one of the items, I want to pass a reference to the selected item, as well as the rest of the list items to another fragment. My question is how should I get all of the items from the adapter? Here are the possibilities that I see:
1. Keep a reference to the backing List
Create the adapter like so:
List<DomainObject> items = new ArrayList<DomainObject>();
listAdapter = new ArrayAdapter<DomainObject>(getActivity(), R.layout.mine, items);
and then simply pass items or a copy of it to the next activity.
The downside I see of this is that I'm relying on the undocumented fact that the same list that I pass to the constructor contains the items later on.
2. Iterate through the adapter
When an item is clicked, iterate through the adapter and build up the list. This seems like an unnecessary amount of work. The items are contained in a List in the adapter and I'm manually copying each item to a new list.
3. Keep a separate list of items when adding to adapter
Before adding an item to the adapter, add it to a separate list that I maintain in the fragment. This is also wasteful as the list of items is copied in the ArrayAdapter and the fragment.
I'm a little late to the game, but I've run up against a similar issue.
One way to deal with #1 would be to maintain the reference to the list within a subclass of ArrayAdapter, so that your reuse is controlled by the adapter object.
Something like:
public class DomainAdapter extends ArrayAdapter<DomainObject> {
private final List<DomainObject> items;
public DomainAdapter(Context context, List<DomainObject> items) {
super(context, R.layout.mine, items);
this.items = items;
}
public List<DomainObject> getItems() {
return items;
}
}
The solution that I've gone with in the meantime is just to not use ArrayAdapter. In cases where you're fighting against this API, it seems like it's better just to use the less fully-featured (and complex) BaseAdapter. You can read more about the decision to go with BaseAdapter instead of ArrayAdapter in this article: Android Adapter Good Practices.
A quick test says that method 1 works. It seems the quickest and cleanest, but since it is undocumented you may want to test it across the intended platforms and whenever they update in case the underlying structure of ArrayAdapter changes.
I am using compile SDK version 22 and min SDK Version 10.
The best method is to "keep a reference to the List" BUT not passing "items" variable/parameter to the Constructor:
List<DomainObject> items = new ArrayList<DomainObject>();
listAdapter = new ArrayAdapter<DomainObject>(getActivity(), R.layout.mine);
In this way you only instantiate the ArrayList as an empty array and you will have to manage YOUR list by yourself.
I think first method is best way to do this.
I dont think, Data would be original for the Another Activity. because, You would pass items through bundle, so the object is written on bundle first and then in next Activity we read from bundle.
However, if you are using some other way to pass the list, use list.clone() to create new Object, instead of passing original one.

Android ListAdapter or Possibly ListView Updating Text at Runtime

Hello folkes I have this little problem for which I cannot find a suitable answer looking around the web and on these forums. Please don't direct me to articles in which people have requested list view text color changes at run time, as I read lots of them and not found one to help me out.
I have a simple ListView that displays an array of String objects via the use of a ListAdapter.
I need to update some of ListView Strings at run time, based on their contents. Using a global reference to the list adapter used in the lists views creation I can get the contents of each list view String using following code below.
However, in addition to retrieval I'd like to be able to modify each string in turn, then put it back in the same index position and have the list view reflect the changes. How?
for (int x = 0; x <= listAdapter.getCount();x++)
{
Object o = this.listAdapter.getItem(x);
if (o.getClass().getSimpleName().equals("String"))
{
String s = (String) o;
s = modifyString(s);
//s is the string I want to modify then put back in the same place.
}//end if
}//end for
As far as I know you cannot change the items in an Adapter - unless you are using a custom Adapter (by extending a BaseAdapter etc...)
So, I think you will have to:
make sure you Adapter's constructor takes in the data structure that holds your strings
make sure your data structure is global
make the changes in that data structure whenever you need to
call myAdapter.notifyDataSetChanged();
This will tell adapter that there were changes done in the list and listview should be recreated.
And after your listview is renewed you can even take the user back to the index by:
list.setSelection(positionWhereTheUserClicked);
I hope this helps, let me know if you need more code references.
Here is some code
private ArrayList<String> results = new ArrayList<String>(); //global
private BaseAdapter searchAdapter = new BaseAdapter (results, this); //global
private void updateResults(final ArrayList<String> updatedList){
results = updatedList;
final ListView list = (ListView)findViewById(R.id.search_results);
list.setAdapter(searchAdapter);
list.setOnItemClickListener(new ListView.OnItemClickListener(){
// implementation of what happens when you click on an item //
});
searchAdapter.notifyDataSetChanged();
}
This code works just fine on my end, I hope it helps.
Just stumbled on this problem and found a solution.
I'm using a
m_ListAdapter = new SimpleAdapter(this, m_List, R.layout.option_list_row, columns, renderTo);
Each item in my listView is a manu option causing a dialog to show, once data is received through the dialog, all I have to do is just create a new SimpleAdapter with an updated ArrayList that includes the new data, then just setAdapter to the new adapter.
The ListView will update instantly.

Categories

Resources