Update all items in a RecyclerView asyncronsously on Android - android

I have a recycler view I want to update one attribute of each item after the initial creation to provide the user with a nicer experience. I would like to do this asynchronously as it takes time to get the data.
How do you iterate over the items in a recycler view and subsequently update. I moved from listview to recycler because it has a the method NotifyItemChanged.
So Ideally I would like to do
void OnRefresh(IList<data> data)
{
Data = data;
NotifyDataSetChanged();
Task.Run(() => UpdateAllAttribute1Fields());
}
void UpdateAllAttribute1Fields()
{
foreach(var myItem in myRecyclerView.Items)
{
UpdateAttribute1(myItem));
}
}
But I do not understand how to access myItems. On windows (sorry), in a listview this would be listview.items I think.
I could save the views OnBindViewHolder but that will be a bit more work.
Thanks all.

You dont need to iterate over all items in recyclerview to update, Just set the RecyclerView to point to your custom data set. And asynchronously update the data set whenever you want. You can then notify the recyclerview that the data in the dataset has changed with the following methods of its adapter (which will automatically update the recyclerview content)
adapterObj.notifyItemChanged(pos) //for one object
adapterObj.notifyDataSetChanged() //for the entire dataset
adapterObj.notifyItemRangeChanged(start, end) //for a range

Related

Get updated data from network with pagination in android

I'm using pagination to get data from network as the only datasource. In the PagedListAdapter I have a button which on click will update the data in the network now I need to get the updated data and change the view for that item. How do I do that?. Is there a way to achieve this without using db?...
lets say I have a Model named Post and it has a field named isSaved. so what I'm doing rt now is when I click the save button in the PagedListAdapter view then I'm calling an api to update that field ..so now I want to get the updated value for that particular post item and change the button's text as saved....since i'm using adapter.submit(listOfPost), the list is the same so DiffUtil itemCallback isn't being called...What should I do??
Suppose when you pass page 1 then take example its returning you 30 items. So all you have to check here is whether recyclerview can scroll vertically or not. If not then increase the page number and call api method again.
And to check whether recyclerView have more item to scroll below method will help you.
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
if(!mRecyclerView.canScrollVertically(1)){
// search the next page
searchNextPage();
}
}
});
For more details this link will be helpful for you: https://github.com/mitchtabian/RestApiMVVM

Is there an event called by changing the count of items in the recyclerview?

Is there an event called by changing the count of items in the recyclerview?
I want to call function every time recyclerview items are added.
Update:
It turns out I misunderstood the original question. What was really asked is how to get notified when the underlying data in an Adapter has changed.
For this you could use a RecyclerView.AdapterDataObserver in conjunction with RecyclerView.Adapter's registerAdapterDataObserver(AdapterDataObserver observer) method to register for changes in the underlying list data.
Original Answer:
Your RecyclerView is backed by a RecyclerView.Adapter which owns the actual list of items being displayed.
If you change the backing list, you should call one of the notifyXXX methods on your adapter to notify your RecyclerView what has changed (and optionally where in the list the change happened.)
At the very least, you could call notifyDataSetChanged on your adapter to tell your RecyclerView that something somewhere in the list has changed. This can be expensive since you're not being specific about what changed, so the RecyclerView has to query the adapter for more information and potentially redraw its entire client area of items.
Something like:
RecyclerView.Adapter adapter = recyclerView.getAdapter();
if (adapter != null) {
adapter.notifyDataSetChanged();
}
Behind the scenes, the RecyclerView will call the RecyclerView.Adapter#getItemCount() method on the Adapter to determine what the current item count is.

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();

Load image via URL into RecyclerAdapter

I have an API which returns JSON and I would like to load an Image from a URL which is provided by this API. The Image should be passed into Adapter for a Recycling View.
Right now all Items which contain an Imgae_URL are getting skipped by my Adapter and I dont really understand why.
if (json_img_url.isNotEmpty()) {
Executors.newSingleThreadExecutor().execute({
val conn = URL(json_img_url).openConnection()
conn.connect()
val iStream:InputStream = conn.getInputStream()
val img_bitmap:Bitmap? = BitmapFactory.decodeStream(iStream)
newItems.add(Item(....img_bitmap))
})
....
itemArrayAdapter.addItems(newItems)
URL :"https://s3.us-east-2.amazonaws.com/c...."
The URls used are valid and Images on the S3 Bucket are all public.
The If statment returns true (I checked with Log.d) but the Item does not appear on the Phone, I dont recive an error and the app does not crash its just like the Item was never there...
I know there are librarys like Picasso or Glide but even with them I could not make it work and to be honest I would like to accomplish this task without a having to install an extra package, it just feels wrong.
Unlike ListView, there is no way to add or remove items directly through the RecyclerView adapter. You need to make changes to the data source directly and notify the adapter of any changes.There are many method available to use when notifying the adapter of different changes:
notifyItemChanged(int pos) : Notify that item at position has changed.
notifyItemInserted(int pos): Notify that item reflected at position has been newly inserted.
notifyItemRemoved(int pos): Notify that items previously located at position has been removed from the data set.
notifyDataSetChanged(): Notify that the dataset has changed. Use only as last resort.
Every time we want to add or remove items from the RecyclerView, we will need to explicitly inform to the adapter of the event. Unlike the ListView adapter, a RecyclerView adapter should not rely on notifyDataSetChanged() since the more granular actions should be used. See the API documentation for more details.
Also, if you are intending to update an existing list, make sure to get the current count of items before making any changes. For instance, a getItemCount() on the adapter should be called to record the first index that will be changed.
// record this value before making any changes to the existing list
int curSize = itemArrayAdapter.getItemCount();
// update the existing list
newItems.add(Item(....img_bitmap));
// curSize should represent the first element that got added
// newItems.size() represents the itemCount
itemArrayAdapter.notifyItemRangeInserted(curSize, newItems.size());

Android: Possible solution for updating the custom list view at random times

I am having a situation where I want to update my Custom List View using BaseAdapter whenever my Database is updated. I have tried calling invalidate() on this Custom List but it didn't work, similarly I even tried having a timer to update my list after sometime, that didn't work either. Please let me know of possible solution.
Update:
This is how I am making my custom list view
li= (ListView)findViewById(R.id.id_lv_row);
ColorDrawable divcolor = new ColorDrawable(Color.DKGRAY);
registerForContextMenu(li);
li.setDivider(divcolor);
li.setDividerHeight(2);
li.setAdapter(new FriendsPositionAdapter(this));
BaseAdapter.notifyDataSetChanged() should do the trick as long as the data behind the adapter actually changed. That's all you need to do to refresh the list.
Invalidate is for repainting views only, you have to tell to the List adapter (BaseAdapter) that dataset has changed.
When the data changes, asign the new dataset to the adapter, and later call notifyDataSetChanged()...
in order to make functional notifyDataSetChanged() the adapter data must be changed. Remember that the original data that change is not reflected automatically to the adapter.
//here i retrieve the new list, named "beans"
lista = (BeanList) result.getDataObject();
Vector<Bean>beans = list.getBeanList();
((BeanListAdapter)listAdapter).syncData(beans);
((BeanListAdapter)listAdapter).notifyDataSetChanged();
//now the syncData method
public void syncData( List<PINPropiedad> newData ){
for(Object o : newData){
add(o);
}
}

Categories

Resources