ListView – what is this?
ListView is a widget which we can use to create a scrolling list. It has be included in the Android SDK since the API 1. Nowadays, for this purpose, we should use a RecyclerView because basically, it’s more flexible and efficient than a ListView. To implement a ListView, we have to create the following:
- of course, the ListView itself, which we should add to our screen layout,
- a layout for each row in the list,
- an adapter which holds the data and binds them to the list.
How to create a ListView
First of all, we should add the ListView to our screen layout and then find it in the Activity.
Now we have the ListView object in our Activity. Cool. But it isn’t used yet. We have to create an adapter for our data and set it to the ListView object. But first, let’s create a layout for each list row.
How to create a list row layout
Layout for the list row is a layout, just like other one.
Here, we just want to show some avatar with text next to it. To do this, we used horizontal LinearLayout. You’ve probably noticed the warning on the LinearLayout. If we place the pointer on it, we’ll see the message.
Basically, it means that instead of using ImageView and TextView separately, we should use another solution, i.e. the
android:drawableLeft attribute in TextView, because it’s more efficient. I didn’t do it that way because I wanted to make it as simple as I can and I’m able to change the icon size inside the ImageView with ease. Of course, you can also try and put some image straight to the TextView.
android:padding attribute is used in the LinearLayout. If you aren’t familiar with this attribute, you should take a look at my short article about it.
The last thing we need to do is to create an adapter.
How to create a ListView adapter
To create an adapter, we’ll extend the ArrayAdapter.
All we need to do is to override the
getView() method to set our list values to the list item layout. Simple as that.
Firstly, the list item layout needs to be inflated and then we can set some text on the TextView. The values list is passed by the constructor — you can generate some fake ones just to see the result on the screen. We’ll get to the warning on the inflating part in a moment.
Here’s the code in one piece.
Now, we can build the project and see the results.
Cool, it’s working and the implementation isn’t that difficult. But there is one thing that we have to improve — the warning inside the
getView() method. Hmmm. But what is the
getView() method actually doing?
This method is called whenever a new list row is going to be visible. That means that the adapter is not creating each row for all of the data at once. Not all of them are visible right from the beginning. Through scrolling, some of them turn to be visible and some of them become invisible. Let’s use the Log to check this out.
Let’s build the project and see what’s happening.
As we expected, the adapter created views for just the visible rows! During scrolling, it’s creating the next ones just before they show up. So, we’re inflating the whole
list_item_view layout for each row of the list! Our view is pretty simple, but let’s imagine what will happen with the more complicated views, when a user will be scrolling the list very fast and the data set will be very large.
This is exactly what the warning is telling us.
So let’s improve this!
NEED A SUCCESSFUL TEAM?
We’re 100% office based team with 7-years’ experience
in mobile & web app development
How to improve the scrolling of the ListView
Basically, we should use the ViewHolder pattern to make our scrolling list smoother. Hmmm. But the part of the warning message says:
You should avoid unconditionally inflating a new layout
So maybe let’s just get rid of the inflating part of each row and it should help.
Basically, we’re inflating the layout just when the
convertView parameter is null. What exactly is the
convertView? Following this documentation:
The old view should be reused, if possible. Note: you should check that this view is non-null and of an appropriate type before using. (…)
Our list has just one type of view, so checking just if it’s non-null is ok in this case.
Great, so we’ve solved the problem? Not exactly. As you can see, we’re still calling the
findViewById() method for each row. This is also expensive to call that frequently. So, how can we fix this? This is the moment when the ViewHolder pattern is coming to the rescue!
How to implement the ViewHolder pattern
The ViewHolder stores list item views — in our case, just a TextView — and it’s accessible via the view tag. So basically, when the
convertView is null, the
list_item_view layout is inflated, the TextView is assigned to the one inside the ViewHolder and the ViewHolder is stored as a view tag inside the row view. Thanks to this, if the row view is reused, we just need to get the ViewHolder and set the proper text on the TextView.
Now, scrolling is pretty efficient.
Other helpful resources:
The ListView is a bit more complicated to implement than views like TextView or Button. Doing it in an optimal way is also not that trivial. We’ve learned how to implement the ListView, bind the data with the adapter and how to improve scrolling using the ViewHolder pattern.
It’s also worth mentioning that the ListView is kind of deprecated because the RecyclerView was introduced with the API 21 (Android Lollipop). I encourage you to use the new one because a couple of things were changed for the better (i.e. the ViewHolder pattern is forced there). I hope you liked it! Stay safe!
Check out my article about the RecyclerView implementation.
The post was written by Mateusz Budzar, Droids On Roids Android Developer. This and other articles of Mateusz you can find also on Medium