Recyclerview.notifyItemInserted() duplicates the list item - android

I have a requirement, where I should download the ad item while scrolling and update the list. Since calling notifyDatasetChnaged(), resets everything, I'm calling notifyItemInserted(position). But, calling this duplicated the items in the list. I found that there are no repeated items in the list. But after calling notifyItemInserted, it duplicates the item. I'm not getting how to resolve this issue. This what I'm doing:
mNewsList.add(mPreviousAdPosition, newsItem);
mAdapter.notifyItemInserted(mPreviousAdPosition);
If I call, it works properly, there are no repeated items. But I don't want my list items to recreate. What can be the issue ?

I had the same problem for exactly the same use case, the solution is:
Implement this method in your Adapter :
#Override
public long getItemId(int position) {
//Return the stable ID for the item at position
return items.get(position).getId();
}
Call this method in the Constructor of your Adapter :
//Indicates whether each item in the data set can be represented with a unique identifier
setHasStableIds(true);

You can add the object at the end of the array with each object having a position along with it where it needs to be shown in the recycler view. Sort this array on the basis of position before calling notifyItemInserted(position). In this way only required data will be drawn.I have recenlty followed this approach and works very well with dynamic sections added in between in recycler view.

You should add the item at the end of the list.
mNewsList.add(newsItem);
and then notify like this.
mAdapter.notifyItemInserted(mNewsList.size()-1);

Create a temporary list and add items as mentioned below:
List<YourModel> mTmpList = new ArrayList<YourMdel>();
//add items (from 0 -> mPreviousAdPosition) to mTmpList;
for(int i=0; i<mPreviousAdPosition; i++) {
mTmpList.add(mNewsList.get(i));
}
//add item at mPreviousAdPosition
mTmpList.add(newsItem);
//add remaining items and set i<=mNewsList.size() because we ha
for(int i=mPreviousAdPosition; i<=mNewsList.size(); i++) {
mTmpList.add(mNewsList.get(i - 1)); //because we have added item at mPreviousAdPosition;
}
mNewsList = mTmpList;
mAdapter.notifyDataSetChanged();

You code should be written like this:
public class RecyclerViewAdapter extends RecyclerView.Adapter{
...
public void addData(int position, Item newsItem) {
mNewsList.add(position, newsItem);
notifyItemInserted(position);
}
...
}
and then you need to call the fun addData

Related

How to sort the RecyclerView on each new item?

I have a RecyclerView on which i add items from another RecyclerView, each item has a property called "TYPE" and it's value can be "FASE1", "FASE2" and up to "FASE8".
When a new item is added to that list or removed i need to sort it based on the TYPE values.
So all items has to be sorted like ITEM1 FASE1 > ITEM2 FASE2 ....
Till now i was just adding or removing items from the RecyclerView like the following:
Here is the code from RecyclerView Adapter of the RecyclerView which adds items to the other RecyclerView.
private void addOrRemove(int position, boolean add) {
// piattiItems is a reference to ArrayList<ItemPTERM> from the Adapter of the RecyclerView where i have to add the items
Item prodotto = mFilteredList.get(position); // getting item from current RecyclerView
ItemPTERM prodottoAggiunto = piattiAdapter.getItemByCode(prodotto.getCodice()); // cheking if there is yet the same item in the RecyclerView where i have to add it
if (add) {
piattiItems.add(nuovoProdotto(prodotto)); // adding new item
piattiAdapter.notifyItemInserted(size);
recyclerPiatti.scrollToPosition(size);
}else {
piattiItems.remove(prodottoAggiunto); // removing item
piattiAdapter.notifyItemRemoved(position);
}
}
The idea is:
RecyclerView should just be responsible for displaying your data, it shouldn't know the order of your List data.
Your List data should be responsible for the order.
So, after you add new item to your RecylerView, first update/resort your List, then call notifyDataSetChanged() on the Adapter object. Or just call sumitList() if you're using ListAdapter with DiffUtil, which will just update the changed items rather than updating the whole List like RecyclerView.Adapter.
It can be done by defining an order relation among the ItemPTERMs by implementing the Comparable<ItemPTERM> interface with ItemPTERM or by creating a Comparator that implements Comparator<ItemPTERM>.
In the latter, you should implement the compare method. I'm supposing that "TYPE" is an Enum to make things simpler; by the way if it's a String you should implement a specific algorithm for your goal.
When comparator is ready, you can add elements to piattiItems as always and call Collections.sort(piattiItems, comparator) to sort the ArrayList. Now you can get the index of the newly added item and use it to tell your RecyclerView the position of this item. RecyclerView will do the rest by showing the item at the correct position!
private Comparator<ItemPTERM> mComparator = new Comparator<>(){
#Override
public int compare(ItemPTERM o1, ItemPTERM o2) {
return o1.type.compareTo(o2.type); // supposing that type is enum
}
}
private void addOrRemove(int position, boolean add) {
Item prodotto = mFilteredList.get(position);
ItemPTERM prodottoAggiunto = piattiAdapter.getItemByCode(prodotto.getCodice());
if (add) {
ItemPTERM newItem = nuovoProdotto(prodotto);
piattiItems.add(newItem);
Collections.sort(piattiItems, mComparator); // sorts after adding new element
int index = piattiItems.indexOf(newItem); // gets the index of the newly added item
piattiAdapter.notifyItemInserted(index); // uses the index to tell the position to the RecyclerView
recyclerPiatti.scrollToPosition(index);
}else {
piattiItems.remove(prodottoAggiunto);
piattiAdapter.notifyItemRemoved(position);
}
}

Android RecyclerView infinity scrolling: how to add more items to dataset

I have implemented my RecyclerView and even added an onscrolllistener to support infinity scrolling and now I'm stuck with a, hopefully, easy problem: How can I add the newly loaded data to the existing dataset?
My current approach: I create a new array with the length of the existing dataset + the length of the newly loaded data. I System.arraycopy my existing dataset and add the new content with a for-loop.
This works but the list is always reset (scrolls back to the top) and I assume my way to add additional content is overly complicated/wrong, though the tutorials I have looked at seem to pass over this "detail".
Update: I'm currently calling "scrollToPosition" on the UI-Thead after the data has been loaded, but I doubt this is the correct way of doing this or am I wrong?
You shouldn't be adding stuff to your dataset, you will sooner or later run out of memory. What you can do is return a big number (I used Short.MAX_VALUE) item in getItemCount inside your adapter and in the method that requests a view for postion you should do position % list.size();
It is not a truly endless RecyclerView this way, but good enough. I will paste some code tomorrow, I don't have it here now :/
I think you have to add items inside your adapter. Let`s say
class Adapter extends Recycler.Adapter<Recycler.ViewHolder>{
List<YourCustomObject> list;
public Adapter(){
list = new ArrayList<>();
}
public void addItem(YourCustomObject item){
list.add(item);
notifyItemDateSetChanged(); //This method for adapter to notice that list size have been changed
}
// Here your views
}
There is implementation of Your fragment or Activity where you retrieve data from internet.Let` say
class MainActivity extends AppCompactActivity{
Adapter adapter = new Adapter();
List<YourCustomObjects> objects;
public void onCreateView(){
//////// Something yours
}
public void onLoadMore(){
///// Your operation to retrieve data and init it to your list objects
for(YourCustomObject object : objects){
adapter.addItem(object);
}
}
}

RecyclerView change data set

I want to implement search functionality for my RecyclerView. On text changed i want to change the data that are displayed with this widget. Maybe this question has been asked before or is simple, but I don't know how the change the data that is to be shown...
My RecyclerView is defined as follows:
// 1. get a reference to recyclerView
mRecyclerView = (RecyclerView)findViewById(R.id.recyclerView);
// 2. set layoutManger
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
// 3. create an adapter
mAdapter = new ItemsAdapter(itemsData);
// 4. set adapter
mRecyclerView.setAdapter(mAdapter);
And the data that I am showing is something like:
ItemData itemsData[] = { new ItemData("Mary Richards"),
new ItemData("Tom Brown"),
new ItemData("Lucy London")
};
So when when I want to give the adapter another set of data, another array (with one item for example), what should I do?
If you have stable ids in your adapter, you can get pretty good results (animations) if you create a new array containing the filtered items and call
recyclerView.swapAdapter(newAdapter, false);
Using swapAdapter hints RecyclerView that it can re-use view holders. (vs in setAdapter, it has to recycle all views and re-create because it does not know that the new adapter has the same ViewHolder set with the old adapter).
A better approach would be finding which items are removed and calling notifyItemRemoved(index). Don't forget to actually remove the item. This will let RecyclerView run predictive animations. Assuming you have an Adapter that internally uses an ArrayList, implementation would look like this:
// adapter code
final List<ItemData> mItems = new ArrayList(); //contains your items
public void filterOut(String filter) {
final int size = mItems.size();
for(int i = size - 1; i>= 0; i--) {
if (mItems.get(i).test(filter) == false) {
mItems.remove(i);
notifyItemRemoved(i);
}
}
}
It would perform even better if you can batch notifyItemRemoved calls and use notifyItemRangeRemoved instead. It would look sth like: (not tested)
public void filterOut(String filter) {
final int size = mItems.size();
int batchCount = 0; // continuous # of items that are being removed
for(int i = size - 1; i>= 0; i--) {
if (mItems.get(i).test(filter) == false) {
mItems.remove(i);
batchCount ++;
} else if (batchCount != 0) { // dispatch batch
notifyItemRangeRemoved(i + 1, batchCount);
batchCount = 0;
}
}
// notify for remaining
if (batchCount != 0) { // dispatch remaining
notifyItemRangeRemoved(0, batchCount);
}
}
You need to extend this code to add items that were previously filtered out but now should be visible (e.g. user deletes the filter query) but I think this one should give the basic idea.
Keep in mind that, each notify item call affects the ones after it (which is why I'm traversing the list from end to avoid it). Traversing from end also helps ArrayList's remove method performance (less items to shift).
For example, if you were traversing the list from the beginning and remove the first two items.
You should either call
notifyItemRangeRemoved(0, 2); // 2 items starting from index 0
or if you dispatch them one by one
notifyItemRemoved(0);
notifyItemRemoved(0);//because after the previous one is removed, this item is at position 0
This is my answer - thanks to Ivan Skoric from his site: http://blog.lovelyhq.com/creating-lists-with-recyclerview-in-android/
I created an extra method inside my adapter class:
public void updateList(List<Data> data) {
mData = data;
notifyDataSetChanged();
}
Then each time your data changes, you just call this method passing in your new data and your view should change to reflect it.
Just re-initialize your adapter:
mAdapter = new ItemsAdapter(newItemsData);
or if you only need to remove add a few specific items rather than a whole list:
mAdapter.notifyItemInserted(position);
or
mAdapter.notifyItemRemoved(position);
If you want to change the complete Adapter in the recycler view. you can just simply set by recycler.setAdapter(myAdapter);
It will automatically remove the old adapter from recycler view and replace it with your new adapter.
As ygit answered, swapAdapter is interesting when you have to change the whole content.
But, in my FlexibleAdapter, you can update the items with updateDataSet. You can even configure the adapter to call notifyDataSetChanged or having synchronization animations (enabled by default). That, because notifyDataSetChanged kills all the animations, but it's good to have for big lists.
Please have a look at the description, demoApp and Wiki pages: https://github.com/davideas/FlexibleAdapter

Remove items from listview on its ItemClickListener

I am using a collection of ArrayList to fill my Listview. My ListView contains two separate rows types.
Header and Footer.
I am trying to achieve the ExpandableListView Functionality on my Listview from which I am trying to remove some items on click of header till next header.
I am using this function to loop through items and removing items
private void removeItems(int value)
{ Log.e(Constant.LOG, items.size()+"");
for (int i = value;i < items.size(); i++) {
if(!items.get(i).isSection())
items.remove(i);
}
Log.e(Constant.LOG, items.size()+"");
adapter = new EntryAdapter(this, items, this);
mListView.setAdapter(adapter);
}
QUESTION IS : I am not able to remove all items from the list in one shot, some stays there !
I have tried looping through adapter.count(); but no luck
My List :
SECTION 1
ITEM 1
ITEM 2
Item N
Section 2
But when I click on Section 1 not all ITEMS get deleted in one shot WHY!
I am not able to use Expandable Listview at this stage because activity contains many more complex functionality on List. Please help me where I am going wrong!
Create a new ArrayList<Collection> , Then add your item in it and then use removeAll(collection).
TRY THIS:
private void removeItems(int value)
{ Log.e(Constant.LOG, items.size()+"");
ArrayList<Collection> deleteItems= new ArrayList<Collection>();
for (int i = value;i < items.size(); i++) {
if(!items.get(i).isSection())
deleteItems.add(items.get(i));
}
items.removeAll(deleteItems);
Log.e(Constant.LOG, items.size()+"");
adapter = new EntryAdapter(this, items, this);
mListView.setAdapter(adapter);
}
EDIT
Every time you are deleting an item, you are changing the index of the elements inside .
e.g : let suppose you are deleting list1 , then list[2] becomes list1 and hence your code will skip list1 next time because now your counter would be moved to 2.
Here are other ways by which you can achieve this also,
Removing item while iterating it
So what exactly I did now. Instead of looping through items again I did like this :
I created another list and parallely populate it with the main array.
items.add(user);
// after populating items did this
newItems.addAll(items); // same collection ArrayList
and finally I can play with the main array by using removeAll and addAll methods.
items.removeAll(newItems); // remove items
items.addAll(afterPosition,newItems); // add items after position

Android listview updating all items

I have a listview with a big list of items.
Now I have a common button "select all" on click of which should do some operation on each item in the list.
I saw the answers about overriding onscrollchanged. But I don't want user to do scrolling. Without that itself all items should be selected.
Currently I am iterating using listview.getcount, but this update for only current view group.
I suppose you have your custom BaseAdapter and overridden getView(..) method refreshes each view according to its state (checked/unchecked). If so, then you just need to make changes to the underlying data of this ListAdapter (let's say, iterate over the array which stores objects which are represented by this list and change some property of each object) and then call notifyDataSetChanged() method.
What is working for me:
private void enableAllListItems() {
for(int i=0;i<mCursorAdapter.getCount();i++)
mCheckedPositions.set(i, true);
mListView.invalidateViews();
}
The import part is the "invalidateViews()", because you have to say the ListView that it's underlying data changed!
But I'm actually using my own Adapter so I overrode the getView()-Method. I'm not sure if it will work with the standard Adapters.
You iterate it using the generic list that you use to populate the list. that would help you.
try this.
private void SelectAll() {
// TODO Auto-generated method stub
int count = this.mainListView.getAdapter().getCount();
for (int i = 0; i < count; i++) {
this.mainListView.setItemChecked(i, true);
}
}

Categories

Resources