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);
}
}
Related
how to store only selected item list data in recycler view from other activity list.
i used this code -- successfully select data but don't know how to add only selected data items in new activity recycler view -
i used this code snippet ite working fine-
//
https://en.proft.me/2018/03/3/multi-selection-recyclerview/
StringBuilder stringBuilder = new StringBuilder();
for (ExcercisesSelectedModel.DataBean data : getList()) {
if (selectedIds.contains(data.getId()))
stringBuilder.append("\n").append(data.getName());
Change
public interface OnClickAction {
public void onClickAction();
}
to
public interface OnClickAction {
public void onClickAction(Item item);
}
In adapter class, on item click
receiver.onClickAction(item);
In Activity class
private List<Item> selectedItem = new ArrayList()
public void onClickAction(Item item) {
selectedItem.add(item);
}
Now use this selected item list in new activity.
Edit: Don't forget to use or rename the interface to Callback, something along the lines of ActivityCallback or OnClickCallback.
This is for the sake of naming conventions.
A simple flag isSelected in parent list will save your day.
So whenever user select/unselect the item just change the value of isSelected flag to true or false.
Now you have a final list from which you can easily identify selected items, simple store in separated list named selectedItemList.
At last use the selectedItemList and fill your Recycleview. Hope it make sense.
I have CustomAdapter which I am using for populating ListView with some data.
Each element in ListView has two variables. For each listview (in onItemClick method) I must check this variables and If they are the same - do some code and If they are different - do another code, for example Toast.makeText(EPG.this, "Variables are different", Toast.LENGTH_SHORT).show();
So I have tried this:
private List<SomeItem> items = new ArrayList();
//items were created
SomeAdapter adapter = new SomeAdapter(this, R.layout.list_item, items);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
for(int i=0; i<=items.size(); i++) {
SomeItem item = items.get(position);
String tmpCI = item.getFirstVariable();
String tmpPCI = item.getecondVariable();
if (!tmpCI.equals(tmpPCI)) {
//some code
} else {
Toast.makeText(EPG.this, "Variables are different", Toast.LENGTH_SHORT).show();
}
}
}
});
But all of my listview elements have values of the first element in those two variables.
So how can I do something like item.next(); for validating all of items in listview?
UPD:
Sorry, I will provide more information about what I am doing after checking variables of listview items for understanding my issue.
I have one more adapter:
SomeAnotherAdapter adapterPr = new SomeAnotherAdapter(this, R.layout.list_tem_another, itemsAnother);
and one more listview:
listViewAnother.setAdapter(adapterPr);
First of all I understood, that first variable should be from first listview and the second variable from another listview.
In this listViewAnother I have many items, which has some "id". For example 1st, 5th and 20th elements have id 90 and other elements have id 100.
We can say, that items from the first listview also have "id".
So I must check if(first variable = second variable) and then show in listViewAnother only items that have id which equals ID from clicked item in listView.
I tried: adapterPr.remove(item2); but then I understood, that I need all of items because I can go back to listView and press another item which will need those removed elements.
Now, hope I provided full information and you will be able to help me improve my code.
Do you need to perform the check on every element of the adapter when you click on one element of the adapter? If not, you don't need a loop. If you do, your loop should be iterating over the original list, and does not need adapter position at all.
In general when using adapters and lists, you should use the adapter's position and the adapter's data set to perform any tasks. It's not good practice to use the adapter position to get an item from the original list.
Simply set one onItemClickListener which gets the corresponding item from the adapter, and do what you need to from there:
private List<SomeItem> items = new ArrayList();
//items were created
SomeAdapter adapter = new SomeAdapter(this, R.layout.list_item, items);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
SomeItem item = adapter.getItem(position);
String tmpCI = item.getFirstVariable();
String tmpPCI = item.getecondVariable();
if (!tmpCI.equals(tmpPCI)) {
//some code
} else {
Toast.makeText(EPG.this, "Variables are different", Toast.LENGTH_SHORT).show();
}
}
});
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
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
I am working on a XMPP based chat in android.. and I am struck at a point where I need to update the position of an item in the listview to the top in case a new.message arrives.
The use case is.. I am on Contacts screen of the app and a new message comes.. so this contact should move to top of the list and get bold. This is what is similar to whatsapp as well
How can this be done. My class imolemebts activity and i have implemented custom list adapter.
So howcan I find if an item exists in the listview and secondly how to dynamically change position
First, keep in mind that a ListView is just a representation of a list of Objects. So if you want to know if an item is in the ListView, you just have to check if the corresponding Object is in your list of Objects.
Is the same when you want to change the position of one item, you have to change the position of the Object in the list.
Start by defining these objects:
private ArrayList<MyObject> lists = new ArrayList<MyObject>();
private MyCustomAdapter myAdapter;
The first time you create your ListView, just do as usually:
//fill your list with your objects
lists.add(myObject1);
lists.add(myObject2);
lists.add(myObject3);
//create and set the adapter
myAdapter = new MyCustomAdapter(..., ..., lists);
myListView.setAdapter(myAdapter);
Now you can know if your lists contains a specific object (which is the same that checking if an item is in your ListView) by simply testing that:
lists.contains(anObject);
Then, if you want to change the position of a specific item in the ListView, you have to create a new list and put the elements in the correct order. You can use something like that (not tested but it should work):
private ArrayList<MyObject> moveItemToTop(ArrayList<MyObject> lists, int positionOfItem) {
if (lists == null || positionOfItem < 0 || positionOfItem >= lists.size()) {
return lists;
}
ArrayList<MyObject> sortedList = new ArrayList<MyObject>();
//add the item to the top
sortedList.add(lists.get(positionOfItem));
for (int i=0; i<lists.size(); i++) {
if (i != positionOfItem) {
sortedList.add(lists.get(i));
}
}
return sortedList;
}
Or even this (which is way easier...).
Finally, call these two methods to update your ListView:
myAdapter = new MyCustomAdapter(..., ..., moveItemToTop(lists, itemPosition));
myListView.setAdapter(myAdapter);
This is how I resolved it
private void moveMessageToTop(MessageObject message) {
int index = 0;
for (Friends friend : mFriends) {
if (friend.getName().equalsIgnoreCase(message.getFrom().split("#")[0])) {
index = mFriends.indexOf(friend);
break;
}
}
if (index != 0) {
mFriends.add(0,new Friends(message.getFrom().split("#")[0], message
.getMessage()));
} else {
Friends frnd = mFriends.get(index);
frnd.setStatus(message.getMessage());
mFriends.add(0, frnd);
mFriends.remove(index);
}
((ListAdapter) lvFriends.getAdapter()).notifyDataSetChanged();
}