When Onbindviewholder Is Called and How It Works

When onBindViewHolder is called and how it works?

Let me start with just a little bit of background (which you may already understand, but it's needed to explain onBindViewHolder()).

RecyclerView is designed to display long lists (or grids) of items. Say you want to display 100 rows of something. A simple approach would be to just create 100 views, one for each row and lay all of them out. But that would be wasteful, because most of them would be off screen, because lets say only 10 of them fit on screen.

So RecyclerView instead creates only the 10 views that are on screen. This way you get 10x better speed and memory usage. But what happens when you start scrolling and need to start showing next views?

Again a simple approach would be to create a new view for each new row that you need to show. But this way by the time you reach the end of the list you will have created 100 views and your memory usage would be the same as in the first approach. And creating views takes time, so your scrolling most probably wouldn't be smooth.

This is why RecyclerView takes advantage of the fact that as you scroll and new rows come on screen also old rows disappear off screen. Instead of creating new view for each new row, an old view is recycled and reused by binding new data to it.

This happens exactly in onBindViewHolder(). Initially you will get new unused view holders and you have to fill them with data you want to display. But as you scroll you'll start getting view holders that were used for rows that went off screen and you have to replace old data that they held with new data.

Every time notifyDataSetChanged() is called, onBindViewHolder() is called

[DiffUtil](https://developer.android.com/reference/androidx/recyclerview/widget/DiffUtil)

It provides a way to calculate difference between the two list and calls related methods on the adapter like notifyItemInserted(), notifyItemRemoved(), notifyItemChanged(), etc. As a result, the whole list doesn’t get refreshed. Only the items that have been changed are refreshed. It also animates the item changes a little bit so it looks quite nice and it is also performance efficient.

Use of ListAdapter which can simplify the use of DiffUtil on a background thread. So you don't even need to call notifyDataSetChanged().

ListAdapter was added in version 27.1.0 and belongs to maven artifact.

Change Your Adapter to Extend ListAdapter and Create a class extends DiffUtil.Itemcallback. here is my code for reference which is in kotlin.

  class AuthorsListAdapter(val clickListner: AuthorClickListner) : ListAdapter<Author, 
AuthorsListAdapter.ViewHolder>(AuthorsDiffCallback()) {

//Draw views
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder.from(parent)
}
//Bind the Values
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(getItem(position)!!, clickListner)
}

class ViewHolder private constructor(val binding:AuthorsListBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(item: Author, clickListner: AuthorClickListner) {
binding.clicklistner = clickListner
binding.authors= item
binding.executePendingBindings()
}

companion object {

fun from(parent: ViewGroup): ViewHolder {
val binding =
AuthorsListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding)
}

}

}

// For Update the differences in list
class AuthorsDiffCallback : DiffUtil.ItemCallback<Author>() {
override fun areItemsTheSame(oldItem: Author, newItem: Author): Boolean {
return oldItem.id == newItem.id
}

override fun areContentsTheSame(oldItem: Author, newItem: Author): Boolean {
return oldItem == newItem
}

}

class AuthorClickListner(val clickListner: (author:Author) -> Unit)
{
fun onClick(author: Author)
{
clickListner(author)
}
}

Let me know if you still have any queries.

Why RecyclerView onBindViewHolder called just once?

At first, Febi Mathew, thank you for trying to help me with this.

I found how to fix it.

My original line in Gradle was

compile 'com.android.support:recyclerview-v7:23.2.0'

after I did sample just for Recycler and it was the same, I tried an older version

compile 'com.android.support:recyclerview-v7:23.1.1'

And it worked perfectly. So I suppose it is not my bug =). If you know how to report for this bug, you welcome to do it.

Update:
What was wrong is I didn't set layout-height for my item as wrap content. And from reason that I don't understand on old version it did not matter but on a new one it just takes all my screen and I didn't think to try to slide it down.

onBindViewHolder is only called for the first element I have clicked in recyclerview

Assuming you are storing your selected items in an ArrayList (which I encourage you to do if you aren't), the solution would be:

public void onBindViewHolder(@NonNull final MilkAdapter1.ViewHolder holder, int position) {
// I need to call this method for all elements in recyclerview whenever restarted
if (c != null && c.moveToFirst()) {
do {
if (nameArrayList.contains(holder.product_name.getText().toString())) { // if present just update the AddToCart status
// if add_to_cart element is present in sqlite Database just update its status
holder.textView.setVisibility(View.GONE); // this will change the Add to Cart status
holder.linearLayout.setVisibility(View.VISIBLE);
holder.digit.setText(quantit + "");
}
} while (c.moveToNext());
c.close();
}
}

As Sachin Soma pointed, you don't need a for loop, since onBindViewHolder will be invoked once for every item in your list.

RecyclerView onBindViewHolder is being called unlimited time for single item

In your createView() you're calling notifyDataSetChanged() which causes the RecyclerView to rebind and update all of it's contents. Part of that process includes calls to adapter.onBindViewHolder() to bind the data set to the individual item views that make up the list. But inside adapter.onBindViewHolder() you have a call to setupLastMessageData() which internally again calls notifyDataSetChanged() which begins the cycle again. This creates an infinite loop of notify->bind->notify->bind

My suggestion is have those firebase listeners outside of your adapter, listen for the necessary changes and updates for your data structure and pass those changes on to the adapter with a single call to notifyDataSetChanged(). You can even make it more efficient by not calling notifyDataSetChanged() and just call the more granular notifyXXX methods() from the adapter if you are able to determine which messages were actually added or changed.

How onBindViewHolder and RecyclerView work?

onBindViewHolder() is call each time you need to fill one entry. If your data size is for example 10, when you scroll the list,the method is going to be called 10 times. If you scroll up again, the RecyclerView will call more times the method updating the data for the view.

The important thing is that you need to manage which data is going to be placed in each position. In your example, x only increment so there will be different data each time the onBindViewHolder() method will be called.

The size of the list is determined by the getItemCount() method.

Hope it helps.

Nested RecyclerView : onBindViewHolder getting called infinitely

I've fixed the issue by setting bottom constraint to main RecyclerView as @Pawel suggested in the comments.

RecyclerView onBindViewHolder only called when getItemViewType changes

I've forgot to implement getItemId when using setHasStableIds(true);

Implementing that solved the issue!



Related Topics



Leave a reply



Submit