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