How to implement a ListView

Mateusz BudzarAndroid Developer

Learn how to implement the ListView, how to bind the data with the adapter and how to improve scrolling using the ViewHolder pattern. All you need to know about implementing a ListView in one place!

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.

Figure 1. Adding a ListView to the activity_main.xml layout

Figure 2. Creating the ListView 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.

Figure 3. Creating a list row layout

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.

Figure 4. Android Studio warning 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.

An android:padding attribute is used in the LinearLayout. If you aren’t familiar with this attribute, you should take a look atmy 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.

Figure 5. Creating an adapter

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.

Figure 6. Implementation of the ListView with a custom adapter

Now, we can build the project and see the results.

Figure 7. Example of scrolling list with the use of ListView

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.

Figure 8. Adding a Log inside the Adapter.getView() method

Let’s build the project and see what’s happening.

Figure 9. Creating the row views by the Adapter.getView() method

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.

NEED A SUCCESSFUL TEAM?

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.

Figure 11. Reusing the row view

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

Figure 12. ViewHolder pattern inside the Adapter.getView() method

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:

Wrap up

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!