Loop in Firebase Recycler Adapter - android

I'm getting the list of user ids from database. And its getting the values correctly. I'm using FirebaseRecyclerAdapter and when I iterate that list in onBindViewHolder the loops runs as the size of list. For example over here :
adapter = new FirebaseRecyclerAdapter<Profiles, DonarViewHolder>(model) {
#Override
protected void onBindViewHolder(#NonNull DonarViewHolder holder, int position, #NonNull Profiles profiles) {
for (int i = 0 ; i<list.size() ; i++){
Toast.makeText(List.this,"List :" +list.get(i), Toast.LENGTH_SHORT).show();
}
}
}
Over here I have 5 item in list and while iterating I'm toasting each item . So in here the loop run for 25 time, its shows each item 5 time in sequence. First it shows 5 items orderly after showing last item it start toasting from first one again and continues till 5 time as the size of list.
So how do I cater this and make it only iterate the whole items for once only?

onBindViewHolder is called by RecyvlerView to display the data at the specified position. Since you have all the data in a List, their corresponding index will be the same as the position of the itemView.
In your function, you are iterating over all the items in the list for each itemView which the ViewHolder is generating.
You have to remove the for loop from your code and simply use the position as the index to retrieve data from your list, whose index is same as position.
Try this code:
adapter = new FirebaseRecyclerAdapter<Profiles, DonarViewHolder>(model) {
#Override
protected void onBindViewHolder(#NonNull DonarViewHolder holder, int position, #NonNull Profiles profiles) {
Toast.makeText(List.this,"List :" +list.get(position), Toast.LENGTH_SHORT).show();
}
}
For this method, every time an itemView is generated, it will be at a position and with this as an index, you can easily get the data from the list.
For your scenario, since you are already inside each itemView being generated inside the onBindViewHolder, and then you are iterating for each element in your list, hence every item is being displayed exactly the same number of times, as the size of the list.

I think you are missing a method that you can use to call each item individually.
create a setter and getter for your item. In your loop you can then call it list.get(position).getItem .

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

Saving the content of recycler view after user has made changes onto it and generate a new list of object

This is how my viewHolder looks like. It has the spinner element. I have called the getItemCount() method using adapter to get the size of the list that is populated on recycler view. Now User can select an item from the spinner. After user has made all the changes i.e. by changing the spinners in all the views needed, i want to retrieve the changed position of spinners for all the viewholders in a array list. I am working on android recyclerview. There is a list of objects that is passed to the adapter and the list is displayed perfectly. Each of my viewholder have three views in it. These are two text views and one spinner. User can select one of the options available from the spinner and that's how my recyclerview will be updated.
I need help for saving the updated list of object i.e., the spinner position selected by the user for all the list items to generate an xml out of it.
my code looks like this:
int size = mAdapter.getItemCount();
int val[]= new int[size]; //till here it's working perfectly fine
for(int i=0; i<size; i++){
View view_temporary = recyclerView.getChildAt(i);
Spinner spin = (Spinner)view_temporary.findViewById(R.id.action);
val[i] = spin.getSelectedItemPosition();
Log.d("OfficeSpace", ""+val[i]);
}
//here activity crashes
I have resolved the issue. I wanted to save the status of my spinner item present in my view holder after scrolling back to the top. Inside my onBind method of recyclerView, i added this code:
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position) {
final Features feature = mFeatures.get(position); //to get the current object from the list
holder.mSpinner.setSelection(feature.getVal());
holder.mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
mFeatures.get(position).setVal(pos);
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}

FirebaseRecyclerAdapter firing twice

I have a problem using FirebaseRecyclerAdapater, at first it was working fine but now this adapter is firing twice. The database reference is only referring one child, but it is always firing twice. The Toast with text "counter" will appear twice
FirebaseRecyclerAdapter<RequestVisit, RequestViewHolder> requestAdapter =
new FirebaseRecyclerAdapter<RequestVisit, RequestViewHolder>(
RequestVisit.class,
R.layout.seekerrequests_layout,
RequestViewHolder.class,
requestDatabase.child("2DBwmhGplGMoAlLy6337HZEShi93")
) {
#Override
protected void populateViewHolder(final RequestViewHolder viewHolder, RequestVisit model, int position) {
Toast.makeText(getContext(), "counter" +
viewHolder.getAdapterPosition(), Toast.LENGTH_SHORT).show();
}
};
requestVisitList.setAdapter(requestAdapter);
A Firebase*Adapter shows a list of items, the child nodes under the location that you attach it to.
If populateViewHolder gets called with two different positions, that means there are two children under requestDatabase.child("2DBwmhGplGMoAlLy6337HZEShi93").
Keep in mind that if 2DBwmhGplGMoAlLy6337HZEShi93 is a child node with two properties, then your approach will call populateViewHolder for each of those properties.
If you want to show only a single item in the RecyclerView, you can create a simple query with:
FirebaseRecyclerAdapter<RequestVisit, RequestViewHolder> requestAdapter =
new FirebaseRecyclerAdapter<RequestVisit, RequestViewHolder>(
RequestVisit.class,
R.layout.seekerrequests_layout,
RequestViewHolder.class,
requestDatabase.orderByKey().equalTo("2DBwmhGplGMoAlLy6337HZEShi93")
)

Recyclerview.notifyItemInserted() duplicates the list item

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

RecyclerView Adapter :OnBindViewHolder

Why do we use something like this to populate the recyclerview with data? Can't we just create a simple array of string like String[] titles and display them, like in ListView?
List< Information> data
public void onBindViewHolder(MyViewHolder holder, int position) {
Information current = data.get(position);
holder.title.setText(current.title);
holder.icon.setImageResource(current.iconId);
}
I recently watched a video on how to work with recyclerview and I can't understand why we have to create another class of information and then again create its array inside onBindViewHolder to show data.
Can anybody help me understand this code?
If you take a look at this example by google, they used an array dataset to populate data in RecyclerView.
Take a look at Google's docs:
Called by RecyclerView to display the data at the specified position. This method should update the contents of the itemView to reflect the item at the given position.
Note that unlike ListView, RecyclerView will not call this method again if the position of the item changes in the data set unless the item itself is invalidated or the new position cannot be determined. For this reason, you should only use the position parameter while acquiring the related data item inside this method and should not keep a copy of it. If you need the position of an item later on (e.g. in a click listener), use getAdapterPosition() which will have the updated adapter position. Override onBindViewHolder(ViewHolder, int, List) instead if Adapter can handle effcient partial bind.
String [] title ;
int [] imageID;
onBindViewHolder(MyViewHolder holder, int position) {
Information current = data.get(position);
holder.title.setText(title [position]); holder.icon.setImageResource(imageID [position]);
}

Categories

Resources