3

If we have N categories (between 1 and 100 according to a REST API) and each with X items (between 1 and 50, depending on the category), what is the best way to do a category RecyclerView?

Schematic image.

To add do this I am adding card views for each item item of a category in the method "onBindViewHolder" like this:

    @Override
public void onBindViewHolder(MoviesViewHolder holder, int i) {
    holder.title.setText(items.get(i).getTitle());

     // I add a CardView (item_category.xml) for each item in the list

    for (ObjectShort object: items.get(i).getObjects()) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(R.layout.item_category, holder.linearCategories, false);
        ImageView img = (ImageView) view.findViewById(R.id.iv_wallpaper);
        imageLoader.load(img, object.getImg().getPoster().getThumbnail());
        holder.linearCategories.addView(view);
    }

}

And when it is recycled I remove the ITEMS elements in "onViewRecycled" because if I do not do it when doing vertical scrolling and reloading a category, items are added again duplicating:

    @Override
public void onViewRecycled(MoviesViewHolder holder) {
    // delete all items 
    holder.linearCategories.removeAllViews();
}

Is there any better way to do it? This is not effective and the scroll is very slow

thanks to all

LordSuricato
  • 105
  • 2
  • 7
  • probably you can add another RecyclerView instead of linearCategories there, this way images will be inflated only when on screen (on in the exactly side) – Marcos Vasconcelos Nov 13 '17 at 22:23

1 Answers1

3

To answer your question, I'm going to go back to the old days of ListView. Let's imagine that I had a simple list where each row was just a title and an image. A really naive implementation of getView() to support that list might look like this:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    View view = inflater.inflate(R.layout.itemview, parent, false);

    TextView title = view.findViewById(R.id.title);
    title.setText("hello world");

    ImageView image = view.findViewById(R.id.image);
    image.setImageResource(R.drawable.my_image);

    return view;
}

There's two major sources of performance trouble in this implementation:

  1. We're inflating a new view every time

  2. We're calling findViewById() every time

Number 1 was solved by making use of the convertView parameter, and number 2 was solved by something called the "view holder pattern". When the Google dev team created the RecyclerView API to replace ListView, they made "view holders" a first-class citizen, and now everyone works with RecyclerView.ViewHolders by default.

It's important to remember that the performance gains of avoiding repeated view inflation and repeated view lookup were so valuable that Google baked them into the new system.

Now let's look at your code...

public void onBindViewHolder(MoviesViewHolder holder, int i) {
    ...
    for (ObjectShort object: items.get(i).getObjects()) {
        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View view = inflater.inflate(R.layout.item_category, holder.linearCategories, false);
        ImageView img = (ImageView) view.findViewById(R.id.iv_wallpaper);
        imageLoader.load(img, object.getImg().getPoster().getThumbnail());
        holder.linearCategories.addView(view);
    }
}

This for loop inside your bind method has all the same problems that my naive getView() implementation above had. Inflating a new view for each item in the category, and invoking findViewById() to get ahold of its ImageView is going to be expensive, especially when the user flings the list.

To fix your performance problems, just apply the same logic to your "inner" list of content as you have to your "outer" content: use a RecyclerView for items (inside each ViewHolder for categories).

Here is a really simple sample app that illustrates my solution: https://gist.github.com/zizibaloob/2eb64f63ba8d1468100a69997d525a54

Ben P.
  • 52,661
  • 6
  • 95
  • 123