optimizing RecyclerView/ListView
Asked Answered
B

1

9

I have a RecyclerView that contains a number of items. Each item is just a TextView, but the padding and font style can change for each item. In terms of the most efficient/smooth scrolling, is it better for me to create a separate layout file for each variable of the item (ie: padding, text style), or can I just set the padding and text style for each item programmatically when the item view is fetched, without having to worry about a performance impact?

Thanks!

Bandanna answered 16/1, 2015 at 22:12 Comment(2)
How many types of view ,you have to use in your list?Lammergeier
I would do it programmatically.Alfy
S
7

In short, the less work you do in the onBindViewHolder() method, the better the performance of your list view scrolling.

The most efficient implementation would simply take the data from the model that is passed and simply set it to the view holder.

Since all of this work in the onBindViewHolder() method is done on the UI thread, offloading any additional work either prior to the binding or offloaded to anthoer thread is beneficial to list view performance.

For example, lets say that you have a list that contains a bunch of tasks as below:

class Task {
    Date dateDue;
    String title;
    String description;

    // getters and setters here
}

Each task has a title, description and a due date associate with it. As a requirement for your app, if the today's date is before the due date, the row should be green and if passed the due date, it should be red. Also the Task object associated with it requires some special date formatting before setting it to the view.

There are two things that will be done in this onBindViewHolder() as each row is rendered onto the screen:

  • You will need to conditionally check each date and compare it against today's date
  • You will need to apply a date formatter to get the view the date in the required specification:

e.g.

class MyRecyclerView.Adapter extends RecyclerView.Adapter {

    static final TODAYS_DATE = new Date();
    static final DATE_FORMAT = new SimpleDateFormat("MM dd, yyyy");

    public onBindViewHolder(Task.ViewHolder tvh, int position) {
        Task task = getItem(position);

        if (TODAYS_DATE.compareTo(task.dateDue) > 0) {
            tvh.backgroundView.setColor(Color.GREEN);
        } else {
            tvh.backgroundView.setColor(Color.RED);
        }

        String dueDateFormatted = DATE_FORMAT.format(task.getDateDue());
        tvh.dateTextView.setDate(dueDateFormatted);
    }
}

In the above, for every row that is rendered, a date comparison is being made. While we were at it, we even took the liberty to make today's date a constant – object creation is possibly one of the most expensive things you can do in a onBindViewHolder() so any optimization is appreciated. Additionally, the date that is passed in the Task object was not in the proper format so it is formatted that on-the-fly as well.

Although this example is trivial, this can quickly balloon into halting the list view scrolling to a crawl. The better way of doing this is to pass an intermediary object, such as a view model that represents the state of the view instead of the actual business model instead.

Instead of passing your Task as the model to the adapter, we create an intermediatary model called TaskViewModel that is created and set to the adapter. Now before setting any information is sent to the adapter, all of the work is done before any view rendering is applied. This comes at the cost of longer initialization time prior to sending data to your RecyclerView but at the better trade-off of list view performance.

This view model instead could be:

public class TaskViewModel {
    int overdueColor;
    String dateDue;
}

Now when tasked to bind the data to the view, we have the actual view state being represented in the view model and our UI thread can continue to be jank free.

public onBindViewHolder(Task.ViewHolder tvh, int position) {
    TaskViewModel taskViewModel = getItem(position);
    tvh.backgroundView.setColor(taskViewModel.getOverdueColor());
    tvh.dateTextView.setDate(taskViewModel.getDateDue());
}
Screwdriver answered 25/6, 2016 at 18:48 Comment(1)
Thanks. I understand the concept behind view recycling. The question I'm asking to do with the padding is more a question of how each view is drawn by the system. If every time the view is drawn onscreen, if the view requests the start padding then by me changing the padding for each row I won't lose performance. If the padding is somehow cached, then I risk a performance hit.Bandanna

© 2022 - 2024 — McMap. All rights reserved.