Android ListView Tutorial with Kotlin

In this tutorial, you’ll learn how to use Android’s ListView to easily create scrollable lists, by creating a simple recipe list app using Kotlin. By Joe Howard.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Optimizing Performance

Whenever you scroll the ListView, its adapter’s getView() method is called in order to create a row and display it on screen.

Now, if you look in your getView() method, you’ll notice that each time this method is called, it performs a lookup for each of the row view’s elements by using a call to the findViewById() method.

These repeated calls can seriously harm the ListView’s performance, especially if your app is running on limited resources and/or you have a very large list. You can avoid this problem by using the View Holder Pattern.

Implement a ViewHolder Pattern

To implement the ViewHolder pattern, open RecipeAdapter and add the following after the getView() method definition:

private class ViewHolder {
  lateinit var titleTextView: TextView
  lateinit var subtitleTextView: TextView
  lateinit var detailTextView: TextView
  lateinit var thumbnailImageView: ImageView
}

As you can see, you create a class to hold your exact set of component views for each row view. The ViewHolder class stores each of the row’s subviews, and in turn is stored inside the tag field of the layout.

This means you can immediately access the row’s subviews without the need to look them up repeatedly.

Now, in getView(), replace everything above (but NOT including) this line:

val recipe = getItem(position) as Recipe

With:

val view: View
val holder: ViewHolder

// 1
if (convertView == null) {

  // 2
  view = inflater.inflate(R.layout.list_item_recipe, parent, false)

  // 3
  holder = ViewHolder()
  holder.thumbnailImageView = view.findViewById(R.id.recipe_list_thumbnail) as ImageView
  holder.titleTextView = view.findViewById(R.id.recipe_list_title) as TextView
  holder.subtitleTextView = view.findViewById(R.id.recipe_list_subtitle) as TextView
  holder.detailTextView = view.findViewById(R.id.recipe_list_detail) as TextView

  // 4
  view.tag = holder
} else {
  // 5
  view = convertView
  holder = convertView.tag as ViewHolder
}

// 6
val titleTextView = holder.titleTextView
val subtitleTextView = holder.subtitleTextView
val detailTextView = holder.detailTextView
val thumbnailImageView = holder.thumbnailImageView

Here’s the play-by-play of what’s happening above.

  1. Check if the view already exists. If it does, there’s no need to inflate from the layout and call findViewById() again.
  2. If the view doesn’t exist, you inflate the custom row layout from your XML.
  3. Create a new ViewHolder with subviews initialized by using findViewById().
  4. Hang onto this holder for future recycling by using setTag() to set the tag property of the view that the holder belongs to.
  5. Skip all the expensive inflation steps and just get the holder you already made.
  6. Get relevant subviews of the row view.

Finally, update the return statement of getView() with the line below.

return view

Build and run. If your app was running a bit slow on the last build, you should see it running smoother now. :]

Where to Go From Here?

You can download the completed project using the download button at the top or bottom of this tutorial.

When you develop for Android, AdapterViews are a common concept that you’ll run into over and over again.

If you want to know more about the inner workings of the ListView and performance details, check out this article on performance tips for Android ListViews.

There are other ways to create lists, such as subclassing a ListActivity and ListFragment. Both of these links take you to the official Android developer site so that you can learn more about how they work.

Both of these alternatives impose the restriction that the respective activity or fragment can only contain a ListView as its child view. Suppose you wanted an activity that had a ListView as well as some other views, it would be impossible with a ListActivity. The same goes for the ListFragment scenario.

And be sure to check out our RecyclerView and Intermediate RecyclerView tutorials to see the more modern way to show lists on Android. Unlike ListView, RecyclerView enforces the use of the ViewHolder pattern and is much more flexible in terms of layout and animation.

Feel free to share your feedback, findings or ask any questions in the comments below or in the forums. Talk to you soon!

Contributors

Odie Edo-Osagie

Author

Over 300 content creators. Join our team.