Android RecyclerView Tutorial with Kotlin

In this Android RecyclerView tutorial, learn how to use Kotlin to display datasets of a large or unknown size! By Kevin D Moore.

4.7 (61) ·

Download materials
Save for later

Update note: Kevin Moore updated this tutorial for Kotlin, Android 28 (Pie) and Android Studio 3.3. Darryl Bayliss wrote the original and Rod Biresch provided the original update.

Recycling is good for the planet. It’s a way to make sure we’re not buried in our own rubbish or lacking resources in the future. Applying this concept to development, Android engineers realized recycling can also make an OS run efficiently. The result? Recycling enthusiasts and developers alike rejoiced at the release of the RecyclerView widget for Android Lollipop. :]

Google announced a support library to make this clean, green recycling machine backwards compatible all the way to Android Eclair (2.2), released in 2010. More celebration!

In this tutorial, you’ll experience the power of RecyclerView in action and learn:

  • The purpose and components of RecyclerView
  • How to change the layout of RecyclerView
  • How to add animations to RecyclerView

For this tutorial, you’ll use the sample app Galacticon to build out a feed of astronomy photos from a public NASA API. Ready to blast off?

Prerequisite note: You’ll need Android Studio 3.0 or greater and a working knowledge of developing for Android with Kotlin before starting this tutorial. Review our introductory tutorials for a refresher!

Getting Started

Get the starter project using the Download Materials at the top or bottom of this tutorial and open it in Android Studio. Click the Run app button at the top and you’ll see this:

It’s empty! Before adding NASA’s amazing astrophotography, you need to do some set up.

Obtaining The API Keys

To use the Astronomy Picture of the Day API, one of NASA’s most popular web services, you need an API key.

To get a key, put your name and email address into and copy the API key that appears on the screen or in your email.

Next, copy the API key and open the strings.xml file in your project. Paste the API key into the api_key string resource, replacing INSERT API KEY HERE:

4. API_KEY paste

RecyclerView 101

You’re about to explore the vastness of RecyclerViews, and no competent commander heads into the unknown without preparation. Consider the next section your mission brief.

Android used to use ListView or GridView classes for displaying lists. A RecyclerView can be thought of as a combination of a ListView and a GridView. However, in a RecyclerView, there are features that separate your code into maintainable components even as they enforce memory-efficient design patterns.

How could it be better than the tried and tested ListView and GridView? The answers are in the details.

Why Use a RecyclerView?

Imagine you’re creating a ListView with complicated custom items.

You create a row layout for the items and use that layout inside your adapter. You inflate your item layout in getView(), referencing each view with the unique ID you provided in XML to customize and add view logic. You pass that to the ListView, and it’s ready to be drawn on the screen. Or is it?

ListViews and GridViews only do half the job of achieving true memory efficiency. They recycle the item layout, but don’t keep references to the layout children, forcing you to call findViewById() for every child of your item layout every time you call getView().

All this calling around can become processor-intensive, especially for complicated layouts. Furthermore, the situation can cause your ListView scrolling to become jerky or nonresponsive as it tries to grab view references.


Android initially provided a solution to this problem on the Android Developers site with smooth scrolling via the power of the View Holder pattern.

With this pattern, a class becomes an in-memory reference to all the views needed to fill your layout. You set the references once and reuse them, working around the performance hit that comes with repeatedly calling findViewById().


Take note: This is an optional pattern for a ListView or GridView. If you’re unaware of this detail, then you may wonder why your ListViews and GridViews are so slow.

RecyclerView and Layouts

The arrival of the RecyclerView changed everything. It still uses an Adapter to act as a data source; however, you have to create ViewHolders to keep references in memory.

To provide a new view, RecyclerView either creates a new ViewHolder object to inflate the layout and hold those references, or it recycles one from the existing stack.

Now you know why it’s called a RecyclerView!

Another perk of using RecyclerViews is that they come with default animations that you don’t have to create or add yourself.

Because it requires a ViewHolder, the RecyclerView knows which animation to apply to which item and adds them as required. You can also create your own animations and apply them as needed.

The last and most interesting component of a RecyclerView is its LayoutManager. This object positions the RecyclerView’s items and tells it when to recycle items that have transitioned off-screen. The ListView used to do this work alone. The RecyclerView has broken out this functionality to allow for different kinds of layouts: Vertical, horizontal, grid, staggered or your own!

Layout Managers offer three choices by default:

  • LinearLayoutManager positions items to look like a standard ListView
  • GridLayoutManager positions items in a grid format similar to a GridView
  • StaggeredGridLayoutManager positions tems in a staggered grid format.

Create your own LayoutManagers to use with a RecyclerView if you want extra customization.

Those are the basics of RecyclerView. Now, on to the mission!

Creating the RecyclerView

To create the RecyclerView, break the work into four parts:

  1. Declare the RecyclerView in an activity layout and reference it in the activity Kotlin file.
  2. Create a custom item XML layout for RecyclerView for its items.
  3. Create the view holder for view items, connect the data source of the RecyclerView and handle the view logic by creating a RecyclerView Adapter.
  4. Attach the adapter to the RecyclerView.

Step one should be familiar. Open up the activity_main.xml layout file and add the following as a child of the LinearLayout:


Here, you’re setting up the layout and telling the RecyclerView to match its parent.

Note: You’re using the v7 support library for backwards compatibility with older devices. The starter project already adds the RecyclerView Support Library as a dependency in your app’s build.gradle file. If you want more information on how to do it yourself, check out the Android developer website.

Pro tip: You’re adding code that requires imports. Make imports easier by setting Android Studio preferences to auto-add imports.

Open MainActivity.kt and declare the following property at the top of the class:

private lateinit var linearLayoutManager: LinearLayoutManager

In onCreate(), add the following lines after setContentView:

linearLayoutManager = LinearLayoutManager(this)
recyclerView.layoutManager = linearLayoutManager

Android Studio should prompt you to import* for recyclerView.

Note: You may wonder how you have a reference to recyclerView without first finding findViewById(). As configured, the project uses the Kotlin Android Extensions plugin. This plugin allows for importing views in a layout as synthetic properties.

The recyclerView is now an extension property for Activity, and it has the same type as declared in activity_main.xml. The plugin removes a lot of boilerplate code and reduces the risk of potential bugs.

Phase one is complete! You’ve declared and allocated memory for two parts of the puzzle that RecyclerViews need to work: The RecyclerView and its Layout Manager.

Laying Out RecyclerView Items

Phase two involves creating a custom layout for the item you want the RecyclerView to use. It works the same way as creating a custom layout for a ListView or Gridview.

Go to your layout folder and create a new layout with the name recyclerview_item_row, making sure the root element is a ConstraintLayout.

Note: ConstraintLayout is Android’s latest layout and is an ultra-uber Layout. It’s similar to a RelativeLayout but can have chained items, gridlines and barriers. Check out more about ConstraintLayouts.

In your new layout, change the ConstraintLayout to look like this:

< xmlns:android=""

Add the following XML elements as children of the ConstraintLayout:

        app:layout_constraintVertical_bias="0.74" />

        tools:text="Some date" />

        app:layout_constraintTop_toBottomOf="@+id/itemDate" />

No rocket science here: You declared a few views as children of your layout. Now you can use them in your adapter.

Adapters for RecyclerView

Right-click on the com.raywenderlich.galacticon folder, select New ‣ Kotlin File ‣ Class, name it RecyclerAdapter and select Class for Kind.

Make the class extend RecyclerView.Adapter as in the following:

class RecyclerAdapter : RecyclerView.Adapter<RecyclerAdapter.PhotoHolder>()  {

Android Studio will prompt you to import the RecyclerView class. Click on RecylerView and press Option-Return (or Alt-Enter on a PC) and choose Import. Since you’re extending a class that has required methods, Android Studio will underline your class declaration with a red squiggle.

To resolve this, click on the line of code to insert your cursor and press Option-Return (or Alt-Enter on a PC) to bring up a context menu. Select Implement Methods:

Select all three methods and press OK to implement the suggested methods:

These methods are the driving force behind your RecyclerView adapter. Note there is still a compiler error for the moment. That’s because your adapter and the required methods are defined using your ViewHolder class, PhotoHolder, which doesn’t exist just yet. You’ll get to define your ViewHolder and see what each required method does soon. Hang tight!

As with every adapter, provide the corresponding view a means of populating items and deciding how many items there should be.

Previously, a ListView’s or GridView’s onItemClickListener managed item clicks. A RecyclerView doesn’t provide methods like this because its focus is ensuring the position and management of the items within.

The job of listening for actions is now the responsibility of the RecyclerView item and its children. This may seem like more overhead, but in return, you get fine-grained control over how your item’s children can act.

At the top of your RecyclerAdapter class, add a variable photos in the primary constructor to hold your photos:

private val photos: ArrayList<Photo>

So it looks like:

class RecyclerAdapter(private val photos: ArrayList<Photo>) : RecyclerView.Adapter<RecyclerAdapter.PhotoHolder>() {

Nice job, Commander! Your adapter now knows where to look for data. Soon you’ll have an ArrayList of photos filled with the finest astrophotography!

Next, populate the stubbed methods that Android Studio added.

The first method, getItemCount(), should be familiar if you’ve worked with ListViews or GridViews.

The adapter will work out how many items to display. In this case, you want the adapter to show every photo you’ve downloaded from NASA’s API. Add update getItemCount() to the following:

override fun getItemCount() = photos.size

Next, you’re going to exploit the ViewHolder pattern to make an object that holds all your view references.

Keeping Hold of Your Views

To create a PhotoHolder for your view references, you’ll create a nested class in your adapter. You’ll add it here rather than in a separate class because its behavior is tightly coupled with the adapter.

Add the following code at the bottom of the RecyclerAdapter class:

class PhotoHolder(v: View) : RecyclerView.ViewHolder(v), View.OnClickListener {
  private var view: View = v
  private var photo: Photo? = null

  init {

  override fun onClick(v: View) {
    Log.d("RecyclerView", "CLICK!")

  companion object {
    private val PHOTO_KEY = "PHOTO"

Here’s what the code above does:

  1. Make the class extend RecyclerView.ViewHolder, allowing the adapter to use it as as a ViewHolder.
  2. Add a reference to the view you’ve inflated to allow the ViewHolder to access the ImageView and TextView as an extension property. Kotlin Android Extensions plugin adds hidden caching functions and fields to prevent the constant querying of views.
  3. Initialize the View.OnClickListener.
  4. Implement the required method for View.OnClickListener since ViewHolders are responsible for their own event handling.
  5. Add a key for easy reference to the item launching the RecyclerView.

You should still have a compiler errors with onBindViewHolder and onCreateViewHolder. Change the p0 argument on onBindViewHolder to holder and the p1 to position.

override fun onBindViewHolder(holder: RecyclerAdapter.PhotoHolder, position: Int) {
    TODO("not implemented") //To change body of created functions use File | Settings | File Templates.

Then change the p0 argument on onCreateViewHolder to parent and the p1 to be viewType.

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerAdapter.PhotoHolder {
    TODO("not implemented") //To change body of created functions use File | Settings | File Templates.

Build and run the app again. It’ll look nearly the same because you haven’t told the RecyclerView how to associate the PhotoHolder with a view.

Assembling The Pieces

Sometimes there are no ViewHolders available. In this scenario, RecylerView will ask onCreateViewHolder() from RecyclerAdapter to make a new one. You’ll use the item layout — PhotoHolder — to create a view for the ViewHolder.

You could add the inflate code to onCreateViewHolder(). However, this is a nice opportunity to show a cool Kotlin feature called Extensions.

First, add a new Kotlin file named Extensions.kt to the project and add the following new extension function to the new file:

fun ViewGroup.inflate(@LayoutRes layoutRes: Int, attachToRoot: Boolean = false): View {
    return LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot)

Replace the TODO("not implemented") line between the curly braces in onCreateViewHolder() with the following:

val inflatedView = parent.inflate(R.layout.recyclerview_item_row, false)
return PhotoHolder(inflatedView)

Here you inflate the view from its layout and pass it in to a PhotoHolder. The parent.inflate(R.layout.recyclerview_item_row, false) method will execute the new ViewGroup.inflate(...) extension function to inflate the layout.

Now the object holds onto those references while it’s recycled, but there are still more pieces to put together before launching your rocket.

Start a new activity by replacing the log in PhotoHolder’s onClick with this code:

val context = itemView.context
val showPhotoIntent = Intent(context,
showPhotoIntent.putExtra(PHOTO_KEY, photo)

This grabs the current context of your item view and creates an intent to show a new activity on the screen, passing the photo object you want to show. Passing the context object into the intent allows the app to know what activity it’s leaving.

Next, add this method inside PhotoHolder:

fun bindPhoto(photo: Photo) { = photo
  view.itemDate.text = photo.humanDate
  view.itemDescription.text = photo.explanation

This binds the photo to the PhotoHolder, giving your item the data it needs to work out what it should show.

It also adds the suggested Picasso import, which is a library that makes it simpler to get images from a given URL.

The last piece of the PhotoHolder assembly will tell it how to show the right photo at the right moment. It’s the RecyclerAdapter’s onBindViewHolder, and it lets you know a new item will be available on screen and the holder needs some data.

Add the following code inside the onBindViewHolder() method:

val itemPhoto = photos[position]

Here, you’re passing in a copy of your ViewHolder and the position where the item will show in your RecyclerView, and calling bindPhoto(...).

That takes care of the assembly. Use the position where your ViewHolder will appear to grab the photo out of your list and then pass it to your ViewHolder.

Step three is complete! Now for the final stage before blast off.

Hooking up the Adapter and RecyclerView

This is the moment you’ve been waiting for. Connect your adapter to your RecyclerView and make sure it retrieves photos when created so you can explore outer space — in pictures.

Open MainActivity.kt, and add this property at the top:

private lateinit var adapter: RecyclerAdapter

Next, underneath the assignment of recyclerView.layoutManager, add the following:

adapter = RecyclerAdapter(photosList)
recyclerView.adapter = adapter

Here you’re creating the adapter, passing in the constructors it needs and setting it as the adapter for your RecyclerView.

Although you connected the adapter, there’s one more thing to do to make sure you don’t have an empty screen.

In onStart(), underneath the call to super, add this code:

if (photosList.size == 0) {

This adds a check to see if your list is empty, and if yes, it requests a photo.

Next, in receivedNewPhoto(), add the following after photosList.add:


So it looks like the following:

override fun receivedNewPhoto(newPhoto: Photo) {
  runOnUiThread {

Here, you inform the recycler adapter that you added an item after updating the list of photos.

Run the app, load up the emulator and before long, Galacticon should look something like this:

7. RecyclerView Working

That’s not all. Tap on the photo, and you should see a new activity that brings that item into focus:

8. Focus Activity

That’s still not all! Try rotating your device or emulator (Function-Control-F11 or F12) and you’ll see the image in full screen glory!

9. Landscape focus

Depending on the size of the image and your device screen it may look a little distorted, but don’t worry about that.

Congratulations! You have a working RecyclerView. Take a journey amongst the stars!

Adding Scrolling Support

If you head back to MainActivity on your device and try to scroll down, you’ll notice something is amiss — your RecyclerView isn’t retrieving any new photos.

10. Scrolling Not Working

Your RecyclerView is doing as it’s told by showing the contents of photosList. The problem is the app will only retrieve one photo. It has no idea when or how to grab more.

To remedy this, retrieve the number of the photos and the last visible photo index while scrolling. Then you’ll check to see that the last photo is visible and there are no photos already on request. If these are both true, then your app downloads more pretty photos!

This patch will require a spacewalk, so break out your spacesuit and get ready for a zero-gravity experience.

In MainActivity.kt, add this property with custom accessor below to MainActivity:

private val lastVisibleItemPosition: Int
  get() = linearLayoutManager.findLastVisibleItemPosition()

This uses your RecyclerView’s LinearLayoutManager to get the index of the last visible item on the screen.

Next, add a method inside of MainActivity that inserts an onScrollListener to your RecyclerView, so it can get a callback when the user scrolls:

  private fun setRecyclerViewScrollListener() {
    recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
      override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
        super.onScrollStateChanged(recyclerView, newState)
        val totalItemCount = recyclerView.layoutManager!!.itemCount
        if (!imageRequester.isLoadingData && totalItemCount == lastVisibleItemPosition + 1) {

This function gives the RecyclerView a scroll listener triggered by scrolling. While scrolling, the listener retrieves the count of the items in its LayoutManager and calculates the last visible photo index. Once done, it compares these numbers, incrementing the index by one because the index begins at zero while the count begins at one. If they match and there are no photos already on request, you request a new photo.

Finally, hook everything to the RecyclerView by calling this method from onCreate beneath where you set your RecyclerView Adapter:


Build and run the app again. Scroll down, and you should see quite an improvement!

11. Scrolling Update

Excellent work! Your RecyclerView now updates to show the latest photo requested by your app. The great thing is that receivedNewPhoto() handles most of the work because you told it to notify your adapter about new items.

That earns an intergalactic thumbs up for upcycling code!

Layout Changes

Now that your RecyclerView is up and running, it’s time to trick out your spaceship.

Wouldn’t it be cool if your RecyclerView could change its layout? Good news: RecyclerView’s item positioning is separated into a layout manager.

Add a property for a GridLayoutManager to the top of MainActivity.kt:

private lateinit var gridLayoutManager: GridLayoutManager

Note that GridLayoutManager is built-in, but you can easily customize it.

In onCreate(), initialize the LayoutManager below the existing LinearLayoutManager:

gridLayoutManager = GridLayoutManager(this, 2)

As with the previous LayoutManager, pass in the context the manager will appear in. Unlike the former, it takes an integer parameter. In this case, set the number of columns the grid will have.

Add this method to MainActivity:

private fun changeLayoutManager() {
  if (recyclerView.layoutManager == linearLayoutManager) {
    recyclerView.layoutManager = gridLayoutManager
    if (photosList.size == 1) {
  } else {
    recyclerView.layoutManager = linearLayoutManager

This code checks to see what LayoutManager your RecyclerView is using, and then:

  1. If it’s using the LinearLayoutManager, it swaps in the GridLayoutManager.
  2. It requests a new photo if your grid layout only has one photo to show.
  3. If it’s using the GridLayoutManager, it swaps in the LinearLayoutManager.

Next, make some changes to lastVisibleItemPosition to help it handle the new LayoutManager. Make it look like the following:

private val lastVisibleItemPosition: Int
  get() = if (recyclerView.layoutManager == linearLayoutManager) {
    } else {

Here, you ask the RecyclerView to tell you what its LayoutManager is. Then you ask that LayoutManager to tell you the position of the last visible item.

To use the grid layout, make use of the Options menu button that is already available in the app. Add the following code underneath onStart():

override fun onOptionsItemSelected(item: MenuItem): Boolean {
  if (item.itemId == {
    return true
  return super.onOptionsItemSelected(item)

This checks the ID of the item tapped in the menu and then determines what to do about it. In this case, there should only be one ID that will match up, effectively telling the app to go away and rearrange the RecyclerView’s LayoutManager.

You’re ready to go! Load the app and tap the button at the top right of the screen. You’ll begin to see the stars shift:

12. Grid Layout

Using ItemTouchHelper

Sometimes you’ll see things you just don’t like, such as a galaxy far, far away that has fallen to the dark side or a planet that is prime for destruction. How could you go about killing it with a swipe?

Luckily, Android has provided the ItemTouchHelper class, which allows for easy swipe behavior. Creating and attaching this to a RecyclerView requires just a few lines of code.

In MainActivity.kt, underneath setRecyclerViewScrollListener() add the following method:

private fun setRecyclerViewItemTouchListener() {

  val itemTouchCallback = object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
    override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, viewHolder1: RecyclerView.ViewHolder): Boolean {
      return false

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, swipeDir: Int) {
      val position = viewHolder.adapterPosition

  val itemTouchHelper = ItemTouchHelper(itemTouchCallback)

Here’s the breakdown, step by step:

  1. Create the callback and tell it what events to listen for. It takes two parameters: One for drag directions and one for swipe directions. You’re only interested in swipe. Pass 0 to inform the callback not to respond to drag events.
  2. Return false in onMove. You don’t want to perform any special behavior here.
  3. Call onSwiped when you swipe an item in the direction specified in the ItemTouchHelper. Here, you request the viewHolder parameter passed for the position of the item view, and then you remove that item from your list of photos. Finally, you inform the RecyclerView adapter that an item has been removed at a specific position.
  4. Initialize ItemTouchHelper with the callback behavior you defined, and then attach it to the RecyclerView.

Add the method to the activity’s onCreate() underneath setRecyclerViewScrollListener():


This will attach the ItemTouchListener to the RecyclerView using the code you just wrote.

Run the app once more and swipe across one of your items. You should see it begin to move. If you swipe the item far enough, you should see it animate and vanish. If other items are visible, they will reorganize themselves to cover the empty space. How cool is that?

13 Swipe Away Item

Where to Go From Here?

Nice job, Commander! Now it’s time to head back to Earth and think about what you’ve learned. In this tutorial, you:

  • Created a RecyclerView and all its necessary components, such as a LayoutManager, an Adapter and a ViewHolder.
  • Updated and removed items from an Adapter.
  • Added some cool features like changing layouts and adding swipe functionality.

Above all, you’ve now experienced how separating components — a key attribute of RecyclerViews — provides so much functionality with such ease. For flexible, exciting collections, look no further than the all-powerful RecyclerView.

Get the final project by using the Download Materials button at the top or bottom of this tutorial.

To learn more about RecyclerViews, check out the Android documentation. Take a look at the support library for RecyclerViews to learn how to use it on older devices. To make them fit with the material design spec, check out the list component design specification.

Join us in the forums to discuss this tutorial and share your findings as you work with RecyclerViews!

Until next time, Space Traveler!