An Android Studio RecyclerView Tutorial

This chapter will create an example project that uses both the CardView and RecyclerView components to create a scrollable list of cards. The completed app will display a list of cards containing images and text. In addition to displaying the list of cards, the project will be implemented such that selecting a card causes messages to be displayed to the user indicating which card was tapped.

Creating the CardDemo Project

Select the New Project option from the welcome screen and, within the resulting new project dialog, choose the Basic Views Activity template before clicking on the Next button.

Enter CardDemo into the Name field and specify com.ebookfrenzy.carddemo as the package name. Before clicking on the Finish button, change the Minimum API level setting to API 26: Android 8.0 (Oreo) and the Language menu to Kotlin.

Modifying the Basic Views Activity Project

Since the Basic Views Activity was selected, the layout includes a floating action button which is not required for this project. Load the activity_main.xml layout file into the Layout Editor tool, select the floating action button, and tap the keyboard delete key to remove the object from the layout. Edit the MainActivity.kt file and remove the floating action button and navigation controller code from the onCreate method as follows:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
 
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
 
    setSupportActionBar(binding.toolbar)
 
    // val navController = findNavController(R.id.nav_host_fragment_content_main)
    // appBarConfiguration = AppBarConfiguration(navController.graph)
    // setupActionBarWithNavController(navController, appBarConfiguration)
 
    // binding.fab.setOnClickListener { view ->
    //    Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
    //        .setAnchorView(R.id.fab)
    //        .setAction("Action", null).show()
    // }
}
Code language: Kotlin (kotlin)

Also, remove the onSupportNavigateUp() method, then open the content_main.xml file and delete the nav_host_ fragment_content_main object from the layout so that only the ConstraintLayout parent remains.

 

You are reading a sample chapter from an old edition of the Android Studio Essentials – Kotlin Edition book.

Purchase the fully updated Android Studio Ladybug Kotlin Edition of this publication in eBook or Print format.

The full book contains 100 chapters and over 878 pages of in-depth information.

Learn more.

Preview  Buy eBook  

 

Designing the CardView Layout

The layout of the views contained within the cards will be defined within a separate XML layout file. Within the Project tool window, right-click on the app -> res -> layout entry and select the New -> Layout Resource File menu option. In the New Resource Dialog, enter card_layout into the File name: field and androidx.cardview. widget.CardView into the root element field before clicking on the OK button.

Load the card_layout.xml file into the Layout Editor tool, switch to Code mode, and modify the layout so that it reads as follows:

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/card_view"
    android:layout_margin="5dp"
    app:cardBackgroundColor="#80B3EF"
    app:cardCornerRadius="12dp"
    app:cardElevation="3dp"
    app:contentPadding="4dp" >
 
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/relativeLayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp">
 
        <ImageView
            android:id="@+id/itemImage"
            android:layout_width="100dp"
            android:layout_height="100dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
 
        <TextView
            android:id="@+id/itemTitle"
            android:layout_width="236dp"
            android:layout_height="39dp"
            android:layout_marginStart="16dp"
            android:textSize="30sp"
            app:layout_constraintLeft_toRightOf="@+id/itemImage"
            app:layout_constraintStart_toEndOf="@+id/itemImage"
            app:layout_constraintTop_toTopOf="parent" />
 
        <TextView
            android:id="@+id/itemDetail"
            android:layout_width="236dp"
            android:layout_height="16dp"
            android:layout_marginStart="16dp"
            android:layout_marginTop="8dp"
            app:layout_constraintLeft_toRightOf="@+id/itemImage"
            app:layout_constraintStart_toEndOf="@+id/itemImage"
            app:layout_constraintTop_toBottomOf="@+id/itemTitle" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>Code language: HTML, XML (xml)

Adding the RecyclerView

Select the content_main.xml layout file and drag a RecyclerView object from the Containers section of the palette onto the layout so that it is positioned in the center of the screen, where it should automatically resize to fill the entire screen. Use the Infer constraints toolbar button to add any missing layout constraints to the view. Using the Attributes tool window, change the ID of the RecyclerView instance to recyclerView and the layout_width and layout_height properties to match_constraint.

Adding the Image Files

In addition to the two TextViews, the card layout contains an ImageView on which the Recycler adapter has been configured to display images. Before the project can be tested, these images must be added. The images that will be used for the project are named android_image_<n>.jpg and can be found in the project_icons folder of the sample code download available from the following URL:

https://www.ebookfrenzy.com/web/giraffekotlin/index.php

 

You are reading a sample chapter from an old edition of the Android Studio Essentials – Kotlin Edition book.

Purchase the fully updated Android Studio Ladybug Kotlin Edition of this publication in eBook or Print format.

The full book contains 100 chapters and over 878 pages of in-depth information.

Learn more.

Preview  Buy eBook  

 

Locate these images in the file system navigator for your operating system and select and copy the eight images. Right click on the app -> res -> drawable entry in the Project tool window and select Paste to add the files to the folder:

Figure 55-1

Creating the RecyclerView Adapter

As outlined in the previous chapter, the RecyclerView needs to have an adapter to handle the creation of the list items. Add this new class to the project by right-clicking on the app -> kotlin+java -> com.ebookfrenzy.carddemo entry in the Project tool window and selecting the New -> Kotlin Class/File menu option. In the new class dialog, enter RecyclerAdapter into the Name field and select Class from the list before tapping the Return keyboard key to create the new Kotlin class file.

Edit the new RecyclerAdapter.kt file to add some import directives and to declare that the class now extends RecyclerView.Adapter. Rather than create a separate class to provide the data to be displayed, some basic arrays will also be added to the adapter to act as the data for the app:

package com.ebookfrenzy.carddemo
 
import android.view.LayoutInflater
import android.widget.ImageView
import android.widget.TextView
import android.view.View
import android.view.ViewGroup
 
import androidx.recyclerview.widget.RecyclerView
 
class RecyclerAdapter : RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() {
 
    private val titles = arrayOf("Chapter One",
            "Chapter Two", "Chapter Three", "Chapter Four",
            "Chapter Five", "Chapter Six", "Chapter Seven",
            "Chapter Eight")
 
    private val details = arrayOf("Item one details", "Item two details",
            "Item three details", "Item four details",
            "Item five details", "Item six details",
            "Item seven details", "Item eight details")
 
    private val images = intArrayOf(R.drawable.android_image_1, 
                  R.drawable.android_image_2, R.drawable.android_image_3, 
                  R.drawable.android_image_4, R.drawable.android_image_5,
                  R.drawable.android_image_6, R.drawable.android_image_7, 
                  R.drawable.android_image_8)
}Code language: Kotlin (kotlin)

Within the RecyclerAdapter class, we now need our own implementation of the ViewHolder class configured to reference the view elements in the card_layout.xml file. Remaining within the RecyclerAdapter.kt file, implement this class as follows:

.
.
class RecyclerAdapter : RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() {
.
.
    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
 
        var itemImage: ImageView
        var itemTitle: TextView
        var itemDetail: TextView
 
        init {
            itemImage = itemView.findViewById(R.id.itemImage)
            itemTitle = itemView.findViewById(R.id.itemTitle)
            itemDetail = itemView.findViewById(R.id.itemDetail)
        }
    }
.
.
}
.
.Code language: Kotlin (kotlin)

The ViewHolder class contains an ImageView and two TextView variables together with a constructor method that initializes those variables with references to the three view items in the card_layout.xml file.

 

You are reading a sample chapter from an old edition of the Android Studio Essentials – Kotlin Edition book.

Purchase the fully updated Android Studio Ladybug Kotlin Edition of this publication in eBook or Print format.

The full book contains 100 chapters and over 878 pages of in-depth information.

Learn more.

Preview  Buy eBook  

 

The next item to be added to the RecyclerAdapter.kt file is the implementation of the onCreateViewHolder() method:

override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
    val v = LayoutInflater.from(viewGroup.context)
            .inflate(R.layout.card_layout, viewGroup, false)
    return ViewHolder(v)
}Code language: Kotlin (kotlin)

This method will be called by the RecyclerView to obtain a ViewHolder object. It inflates the view hierarchy card_layout.xml file and creates an instance of our ViewHolder class initialized with the view hierarchy before returning it to the RecyclerView.

The purpose of the onBindViewHolder() method is to populate the view hierarchy within the ViewHolder object with the data to be displayed. It is passed the ViewHolder object and an integer value indicating the list item that is to be displayed. This method should now be added, using the item number as an index into the data arrays. This data is then displayed on the layout views using the references created in the constructor method of the ViewHolder class:

override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
    viewHolder.itemTitle.text = titles[i]
    viewHolder.itemDetail.text = details[i]
    viewHolder.itemImage.setImageResource(images[i])
}Code language: Kotlin (kotlin)

The final requirement for the adapter class is an implementation of the getItem() method which, in this case, returns the number of items in the titles array:

override fun getItemCount(): Int {
    return titles.size
}Code language: Kotlin (kotlin)

Initializing the RecyclerView Component

At this point, the project consists of a RecyclerView instance, an XML layout file for the CardView instances and an adapter for the RecyclerView. The last step before testing the progress so far is to initialize the RecyclerView with a layout manager, create an instance of the adapter and assign that instance to the RecyclerView object. For the purposes of this example, the RecyclerView will be configured to use the LinearLayoutManager layout option.

 

You are reading a sample chapter from an old edition of the Android Studio Essentials – Kotlin Edition book.

Purchase the fully updated Android Studio Ladybug Kotlin Edition of this publication in eBook or Print format.

The full book contains 100 chapters and over 878 pages of in-depth information.

Learn more.

Preview  Buy eBook  

 

There is a slight complication here because we need to be able to use view binding to access the recyclerView component from within the MainActivity class. The problem is that recyclerView is contained within the content_main.xml layout file which is, in turn, included in the activity_main.xml file. To be able to reach down into the content_main.xml file, we need to assign it an id at the point that it is included. To do this, edit the activity_main.xml file and modify the include element so that it reads as follows:

.
.
   <include
        android:id="@+id/contentMain"
        layout="@layout/content_main" />
.
.Code language: HTML, XML (xml)

With an id assigned to the included file, the recyclerView component can be accessed using the following binding:

binding.contentMain.recyclerViewCode language: Kotlin (kotlin)

Now edit the MainActivity.kt file and modify the onCreate() method to implement the initialization code:

package com.ebookfrenzy.carddemo
.
.
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
 
class MainActivity : AppCompatActivity() {
.
. 
    private var layoutManager: RecyclerView.LayoutManager? = null
    private var adapter: RecyclerView.Adapter<RecyclerAdapter.ViewHolder>? = null
 
    override fun onCreate(savedInstanceState: Bundle?) {
.
. 
        layoutManager = LinearLayoutManager(this)
        binding.contentMain.recyclerView.layoutManager = layoutManager
        adapter = RecyclerAdapter()
        binding.contentMain.recyclerView.adapter = adapter 
    }
.
.
}Code language: Kotlin (kotlin)

Testing the Application

Compile and run the app on a physical device or emulator session and scroll through the different card items in the list:

Figure 55-2

Responding to Card Selections

The last phase of this project is to make the cards in the list selectable so that clicking on a card triggers an event within the app. For this example, the cards will be configured to present a message on the display when tapped by the user. To respond to clicks, the ViewHolder class needs to be modified to assign an onClickListener on each item view. Edit the RecyclerAdapter.kt file and modify the ViewHolder class declaration so that it reads as follows:

 

You are reading a sample chapter from an old edition of the Android Studio Essentials – Kotlin Edition book.

Purchase the fully updated Android Studio Ladybug Kotlin Edition of this publication in eBook or Print format.

The full book contains 100 chapters and over 878 pages of in-depth information.

Learn more.

Preview  Buy eBook  

 

.
.
    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
 
        var itemImage: ImageView
        var itemTitle: TextView
        var itemDetail: TextView
 
        init {
            itemImage = itemView.findViewById(R.id.item_image)
            itemTitle = itemView.findViewById(R.id.item_title)
            itemDetail = itemView.findViewById(R.id.item_detail)
 
            itemView.setOnClickListener { v: View  ->
 
            }
        }
    }
.
.
}Code language: Kotlin (kotlin)

Within the body of the onClick handler, code can now be added to display a message indicating that the card has been clicked. Given that the actions performed as a result of a click will likely depend on which card was tapped, it is also important to identify the selected card. This information can be obtained via a call to the getAdapterPosition() method of the RecyclerView.ViewHolder class. Remaining within the RecyclerAdapter.kt file, add code to the onClick handler so it reads as follows:

.
.
import com.google.android.material.snackbar.Snackbar
.
.
itemView.setOnClickListener { v: View  ->
    val position: Int = adapterPosition
 
    Snackbar.make(v, "Click detected on item $position",
            Snackbar.LENGTH_LONG).setAction("Action", null).show()
}Code language: Kotlin (kotlin)

The last task is to enable the material design ripple effect that appears when items are tapped within Android applications. This involves the addition of some properties to the declaration of the CardView instance in the card_layout.xml file as follows:

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/card_view"
    android:layout_margin="5dp"
    app:cardBackgroundColor="#80B3EF"
    app:cardCornerRadius="12dp"
    app:cardElevation="3dp"
    app:contentPadding="4dp"
    android:foreground="?selectableItemBackground"
    android:clickable="true" >Code language: HTML, XML (xml)

Run the app once again and verify that tapping a card in the list triggers both the standard ripple effect at the point of contact and the appearance of a Snackbar reporting the number of the selected item.

Summary

This chapter has worked through the steps involved in combining the CardView and RecyclerView components to display a scrollable list of card-based items. The example also covered the detection of clicks on list items, including the identification of the selected item and the enabling of the ripple effect visual feedback on the tapped CardView instance.