Have you ever wanted to create an app where you had to display data to users in a list? This could be anything like a to-do list, shopping list, friend list, contact list, etc. One important characteristic of these types of lists is that their data has to be usually displayed in similarly styled containers. For example, if you are creating a contact list you will probably show the name and number of each contact in the list.
There are two very useful views that you can use in Android. These are named the CardView
and the RecyclerView
. The CardView
is useful for showcasing data related to individual items in a list. The RecyclerView
is useful for showcasing a list of those items.
In this tutorial, you will learn how to use both these views together to create a list of people in the neighborhood.
Initial Setup
Open Android Studio and then click on the New Project button. Select Empty Activity and then click the Next button.
On the next screen, set the name of the app to whatever you want. I have set it to RecyclerCard. Keep the language Kotlin as we will be writing Kotlin code which is now the recommended language to use for Android development. Set the Minimum SDK to any value greater than or equal to 23. Click Finish once you are done.
You can also set it to a lower value, but you will then have to add the following dependencies to your build.gradle file.
1 | implementation 'androidx.cardview:cardview:1.0.0' |
2 | implementation 'androidx.recyclerview:recyclerview:1.3.0' |
Creating a CardView
A CardView
is ideal when you want to display your data or item in containers with a similar style. It is basically a FrameLayout
with a rounded corner background and shadow. Native support for CardView
was added in Lollipop where is uses the elevation property for shadows. Older platforms rely on a custom emulated shadow to achieve the same effect.
You can create an empty CardView
by using the following XML:
1 | <androidx.cardview.widget.CardViewxmlns:android="https://schemas.android.com/apk/res/android" |
2 | xmlns:app="http://schemas.android.com/apk/res-auto" |
3 | xmlns:tools="http://schemas.android.com/tools" |
4 | android:id="@+id/card_view" |
5 | android:layout_width="match_parent" |
6 | android:layout_height="160dp"> |
7 | |
8 | </androidx.cardview.widget.CardView> |
Any empty CardView
isn't going to be very useful. We will be using it to display information about the people in our neighborhood. So, let's add some widgets inside our CardView
to display a picture, name, age, and profession.
Our CardView
is going to be one part of the larger app. So, we will place the XML code for the CardView
UI inside a file called item.xml. You can create this file by navigating to the layout folder, and then right clicking on it to add a new Layout Resource File.
The full XML will look something like this:
1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <androidx.cardview.widget.CardViewxmlns:android="http://schemas.android.com/apk/res/android" |
3 | xmlns:app="http://schemas.android.com/apk/res-auto" |
4 | xmlns:tools="http://schemas.android.com/tools" |
5 | android:id="@+id/card_view" |
6 | android:layout_width="match_parent" |
7 | android:layout_height="160dp" |
8 | android:layout_marginStart="20dp" |
9 | android:layout_marginTop="16dp" |
10 | android:layout_marginEnd="20dp" |
11 | app:cardCornerRadius="10dp"> |
12 | |
13 | <RelativeLayout |
14 | android:layout_width="match_parent" |
15 | android:layout_height="wrap_content" |
16 | android:padding="16dp"> |
17 | |
18 | <ImageView |
19 | android:id="@+id/avatar" |
20 | android:layout_width="120dp" |
21 | android:layout_height="120dp" |
22 | tools:srcCompat="@tools:sample/avatars"/> |
23 | |
24 | <LinearLayout |
25 | android:layout_width="match_parent" |
26 | android:layout_height="wrap_content" |
27 | android:layout_marginStart="20dp" |
28 | android:layout_marginTop="20dp" |
29 | android:layout_toEndOf="@+id/avatar" |
30 | android:orientation="vertical"> |
31 | |
32 | <TextView |
33 | android:id="@+id/person_name" |
34 | android:layout_width="wrap_content" |
35 | android:layout_height="wrap_content" |
36 | android:fontFamily="sans-serif-black" |
37 | android:textColor="@color/black" |
38 | android:textSize="24sp" |
39 | tools:text="Michael"/> |
40 | |
41 | <TextView |
42 | android:id="@+id/person_age" |
43 | android:layout_width="wrap_content" |
44 | android:layout_height="wrap_content" |
45 | android:textColor="@color/teal_700" |
46 | tools:text="30 Years"/> |
47 | |
48 | <TextView |
49 | android:id="@+id/person_job" |
50 | android:layout_width="wrap_content" |
51 | android:layout_height="wrap_content" |
52 | android:textColor="@color/purple_700" |
53 | android:textSize="20sp" |
54 | tools:text="Accountant"/> |
55 | |
56 | </LinearLayout> |
57 | </RelativeLayout> |
58 | |
59 | </androidx.cardview.widget.CardView> |
Our CardView
contains a RelativeLayout
widget that contains an ImageView
widget and a LinearLayout
widget. The LinearLayout
widget contains three more TextView
widgets that hold the person's name, age, and profession.
The following image shows the empty as well the populated CardView
widget:
Creating a RecyclerView
Using a RecylcerView
is a bit more complicated than creating a CardView
. This is because simply writing the required XML isn't enough, you have to also write some Kotlin or Java code to populate it with data.
Consider the following XML which goes in our activity_main.xml file. It contains a RecyclerView
below a TextView
.
1 | <?xml version="1.0" encoding="utf-8"?> |
2 | <androidx.constraintlayout.widget.ConstraintLayoutxmlns:android="http://schemas.android.com/apk/res/android" |
3 | xmlns:app="http://schemas.android.com/apk/res-auto" |
4 | xmlns:tools="http://schemas.android.com/tools" |
5 | android:layout_width="match_parent" |
6 | android:layout_height="match_parent" |
7 | tools:context=".MainActivity"> |
8 | |
9 | <TextView |
10 | android:id="@+id/heading" |
11 | android:layout_width="match_parent" |
12 | android:layout_height="wrap_content" |
13 | android:text="MY NEIGHBORS" |
14 | android:textAlignment="center" |
15 | android:layout_marginTop="20dp" |
16 | android:textSize="30sp" |
17 | android:textColor="@color/black" |
18 | android:fontFamily="sans-serif-black" |
19 | app:layout_constraintTop_toTopOf="parent"/> |
20 | |
21 | <androidx.recyclerview.widget.RecyclerView |
22 | android:id="@+id/recycler_view" |
23 | android:layout_width="match_parent" |
24 | android:layout_height="match_parent" |
25 | android:layout_marginTop="80dp" |
26 | app:layout_constraintTop_toBottomOf="@id/heading"/> |
27 | |
28 | </androidx.constraintlayout.widget.ConstraintLayout> |
The preview of this layout file will show you the heading My Neighbors and then a list of items titled item 0, item 1, item 2, and so on. However, installing the app on a device will only show the heading and nothing else. This is because we haven't written any Kotlin code to populate the RecyclerView
.
The RecyclerView library dynamically creates elements based on the data that you supply and the layout information you have provided to present the items. This widget provides significant gains in performance when displaying a large amount of data because it recycles elements that have scrolled off the screen.
Now, we will write some code that populates our RecyclerView
widget.
Creating Helper Classes
Your MainActivity.kt file will already have some code in it. Position your cursor below and outside the MainActivity
class and add the following line:
1 | dataclassPerson(valname:String,valage:Int,valjob:String,valphotoId:Int) |
It will create a new data class to hold data about different people in Person
objects. We are using it to store four pieces of information. The person's name as a string, their age as an integer, their job as a string, and a photoId as an integer.
Now, add the following line inside the MainActivity
class just above the onCreate()
method.
1 | privatevalpersons:MutableList<Person>=mutableListOf() |
This line declares a private and mutable list of Person
objects. Currently, the list is empty, but we will populate it soon. Add the following method inside the MainActivity
class:
1 | privatefuninitializeData(){ |
2 | persons.add(Person("Emma Wilson",23,"Architect",R.drawable.emma)) |
3 | persons.add(Person("Jack Johnson",25,"Comedian",R.drawable.jack)) |
4 | persons.add(Person("Larry James",35,"Cook",R.drawable.larry)) |
5 | } |
We will call this method from within our onCreate()
method in order to populate our persons
list. Here, we are providing the name, age, profession, and avatar of different people. The avatar argument contains a reference to different drawable resources which are simple png files.
Now, we will define another class called RVAdapter
which is a custom adapter class that extends RecyclerView.Adapter
. This class will take our list of Person
objects as its constructor argument. The adapter is responsible for providing views for each item in our list. Here is the complete code for the RVAdapter
class:
1 | classRVAdapter(privatevalpersons:List<Person>): |
2 | RecyclerView.Adapter<RVAdapter.PersonViewHolder>(){ |
3 | |
4 | classPersonViewHolder(itemView:View): |
5 | RecyclerView.ViewHolder(itemView){ |
6 | varpersonName:TextView=itemView.findViewById(R.id.person_name) |
7 | varpersonAge:TextView=itemView.findViewById(R.id.person_age) |
8 | varpersonJob:TextView=itemView.findViewById(R.id.person_job) |
9 | varpersonPhoto:ImageView=itemView.findViewById(R.id.avatar) |
10 | } |
11 | |
12 | overridefungetItemCount():Int{ |
13 | returnpersons.size |
14 | } |
15 | |
16 | overridefunonCreateViewHolder(viewGroup:ViewGroup,viewType:Int):PersonViewHolder{ |
17 | valv:View= |
18 | LayoutInflater.from(viewGroup.context).inflate(R.layout.item,viewGroup,false) |
19 | returnPersonViewHolder(v) |
20 | } |
21 | |
22 | overridefunonBindViewHolder(personViewHolder:PersonViewHolder,idx:Int){ |
23 | personViewHolder.personName.text=persons[idx].name |
24 | personViewHolder.personAge.text="${persons[idx].age} Years Old" |
25 | personViewHolder.personJob.text=persons[idx].job |
26 | personViewHolder.personPhoto.setImageResource(persons[idx].photoId) |
27 | } |
28 | } |
There are a few things to note here. First, we are overriding three different methods called getItemCount()
, onCreateViewHolder()
, and onBindViewHolder()
. It also contains an inner class called PersonViewHolder
which holds reference to the views within each item of the RecyclerView
. In this particular case, it contains the reference to the three different TextView
widgets and one ImageView
widget.
The getItemCount()
method is used internally to determine the number of items. We override this method to provide correct information about the number of items in our list.
The onCreateViewHolder()
method creates and returns a new PersonViewHolder
object for each item in the list. You can see that we are passing the layout file for our CardView
to the inflate()
method here. The onBindViewHolder()
method provides data binding for the views within the view holder.
Updating the onCreate()
Method
Now that we have all the helper code in place, we can update the onCreate()
method for our MainAcitivity
. Here is the code for our implementation of the onCreate()
method:
1 | overridefunonCreate(savedInstanceState:Bundle?){ |
2 | super.onCreate(savedInstanceState) |
3 | setContentView(R.layout.activity_main) |
4 | |
5 | valrecyclerView=findViewById<RecyclerView>(R.id.recycler_view) |
6 | recyclerView.setHasFixedSize(true) |
7 | |
8 | vallinearLayoutManager=LinearLayoutManager(this) |
9 | recyclerView.layoutManager=linearLayoutManager |
10 | |
11 | initializeData() |
12 | |
13 | valadapter=RVAdapter(persons) |
14 | recyclerView.adapter=adapter |
15 | } |
We begin by setting the layout for our activity using the setContentView()
method where we pass our main layout file. After that, we find a reference to our RecyclerView
and then create a layout manager to be used with this view.
We make a call to the initializeData()
method on the next line, and it adds some persons to our empty list. Then, we create a new instance of our RVAdapter
class using our list of persons as an argument. The adapter of our RecyclerView
is then set to the RVAdapter
object we just created.
Run your app now and you should see the something like the image below.
The left screen shows what the app looks like without our adapter. As you can see, the RecyclerView
doesn't render anything unless we provide it data. The image files are from an Avatar icon set on Reshot. These are just png files that you need to place in the drawable folder.
Final Thoughts
In this tutorial, you learned the basics of creating a CardView
and RecyclerView
in android. Both these views are generally used together to display a list of items that contain similar data. RecyclerView
provides a huge performance boost for large lists because it recycles items in your list that have scrolled off the screen. You should prefer using RecyclerView
whenever displaying data in lists.
Our app is just fine if we want to display data to users. However, we cannot interact with the data in current state. In our next tutorial, we will show you how to select multiple items from any RecyclerView list.