How to implement a ListView?
ListView is a widget which we can use to create a scrolling list. It is 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 bind them to the list.
How to create a ListView?
First of all we should add the a 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 and 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 as any other one.
Simply we just want to show some avatar and a 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 i.e.
android:drawableLeft attribute in TextView because it’s more efficient. I didn’t do it in that way because I wanted to make it as simple as I can and I’m able to change the icon size easily inside the ImageView. Of course you can try and put some image straight to the TextView.
android:padding attribute is used in the LinearLayout. If you aren’t familiar with that attribute, you should take a look at my short article about it.
The last thing 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
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 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 implementation isn’t that difficult. But there is one thing that we have to improve — the warning inside the
getView() method. Hmmm. But what the
getView() method is actually doing?
This method is called whenever a new list row is gonna 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 invisible. Let’s use the Log to check this out.
Let’s build the project and see what is happening.
As we expected, the adapter created views just for the visible rows! During the scrolling it’s creating 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!
How to improve scrolling of the ListView?
Basically we should user 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 for 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 the documentation:
The old view to reuse, 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 the 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
findViewById() method for each row. This one is also expensive to call that frequently. So how to fix this? This is the moment when the ViewHolder pattern is going for 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 that, 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.
The ListView is a bit more complicated to implement than views like TextView or Button. Do 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 ViewHolder pattern.
It’s worth to mentioned that the ListView is a 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 better (i.e. the ViewHolder pattern is forced there).
Check out my article about the RecyclerView implementation.
Hope you liked it! Stay save!