Android RecyclerView with Retrofit – Load JSON Array into RecyclerView Step by Step
Today we will learn how to load multiple objects from an API into a RecyclerView.
Earlier, we worked with one object. Now our API returns multiple comment objects like this:
[
{
"postId": 1,
"id": 1,
"name": "id labore ex et quam laborum",
"email": "Eliseo@gardner.biz",
"body": "laudantium enim quasi est quidem magnam voluptate ipsam eos"
},
{
"postId": 1,
"id": 2,
"name": "quo vero reiciendis velit similique earum",
"email": "Jayne_Kuhic@sydney.com",
"body": "est natus enim nihil est dolore omnis voluptatem numquam"
}
]
The important difference is the outer:
[ ]
Square brackets mean:
This API is returning a list or array of objects.
So our flow becomes:
API
↓
JSON Array
↓
List<Comment>
↓
Adapter
↓
RecyclerView
↓
Multiple Rows
1. Understand the JSON First
Look at one object:
{
"postId": 1,
"id": 1,
"name": "id labore ex et quam laborum",
"email": "Eliseo@gardner.biz",
"body": "some comment"
}
This represents one comment.
So we create one Kotlin model class called:
Comment
But our API is returning:
Comment 1
Comment 2
Comment 3
Comment 4
Comment 5
Therefore Android needs:
List<Comment>
not:
Comment
2. Create the Comment Data Class
Create:
data class Comment(
val postId: Int,
val id: Int,
val name: String,
val email: String,
val body: String
)
Each property matches one key from the JSON.
For example:
"name": "id labore ex et quam laborum"
matches:
val name: String
Similarly:
"id": 1
matches:
val id: Int
3. Understand One Object vs Multiple Objects
Earlier we may have written:
fun getPost(): Call<Post>
That means:
I expect ONE Post object.
Now our API returns multiple comments.
So we write:
fun getComments(): Call<List<Comment>>
This means:
Call
↓
List
↓
Comment
In simple words:
This API call will return a list containing Comment objects.
4. Create the API Interface
Suppose we are using JSONPlaceholder.
Create:
interface ApiService {
@GET("comments?postId=1")
fun getComments(): Call<List<Comment>>
}
Let’s understand it.
@GET("comments?postId=1")
means:
Send a GET request to this endpoint.
And:
fun getComments(): Call<List<Comment>>
means:
This request is expected to return multiple Comment objects.
5. Retrofit Client
Create:
object RetrofitClient {
private const val BASE_URL =
"https://jsonplaceholder.typicode.com/"
private val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
val apiService: ApiService =
retrofit.create(ApiService::class.java)
}
The final URL becomes:
https://jsonplaceholder.typicode.com/comments?postId=1
Retrofit will send the request to that URL.
6. What is RecyclerView?
RecyclerView is used when we want to display multiple items.
For example:
Comment 1
Comment 2
Comment 3
Comment 4
Comment 5
Instead of creating five different layouts manually, RecyclerView uses one row layout repeatedly.
Conceptually:
RecyclerView
|
├── Row 1
├── Row 2
├── Row 3
├── Row 4
└── Row 5
7. Create RecyclerView in Activity XML
In activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
This RecyclerView will display all comments.
8. Create One Row Design
RecyclerView needs to know:
What should one item look like?
Create:
item_comment.xml
Add:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Name"
android:textSize="18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="Email" />
<TextView
android:id="@+id/tvBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Comment Body" />
</LinearLayout>
This XML represents only one comment.
RecyclerView will reuse this design for every comment.
9. Understand the Adapter Before Writing It
RecyclerView cannot directly understand:
List<Comment>
It needs an Adapter.
Think:
List<Comment>
↓
Adapter
↓
RecyclerView
↓
Screen
The Adapter acts like a bridge.
Its job is:
Take data
↓
Take row layout
↓
Put data inside row
↓
Give row to RecyclerView
10. Create CommentAdapter
Create:
class CommentAdapter(
private val comments: List<Comment>
) : RecyclerView.Adapter<CommentAdapter.CommentViewHolder>() {
}
Let’s understand this slowly.
private val comments: List<Comment>
means:
This Adapter will receive a list of comments.
Example:
comments
[
Comment 1,
Comment 2,
Comment 3,
Comment 4,
Comment 5
]
11. What is a ViewHolder?
Inside the Adapter create:
class CommentViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
val tvName: TextView =
itemView.findViewById(R.id.tvName)
val tvEmail: TextView =
itemView.findViewById(R.id.tvEmail)
val tvBody: TextView =
itemView.findViewById(R.id.tvBody)
}
The ViewHolder holds references to the views inside one row.
Remember:
item_comment.xml
↓
CommentViewHolder
↓
tvName
tvEmail
tvBody
You can explain:
ViewHolder is holding the TextViews of one RecyclerView row.
12. Why Does RecyclerView Need a ViewHolder?
Imagine there are 1,000 comments.
RecyclerView should not repeatedly run:
findViewById()
for every single piece of data.
ViewHolder keeps references to the views so RecyclerView can reuse rows efficiently.
This is why it is called:
RecyclerView
It recycles previously created item views.
13. onCreateViewHolder()
Now add:
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): CommentViewHolder {
val view = LayoutInflater
.from(parent.context)
.inflate(
R.layout.item_comment,
parent,
false
)
return CommentViewHolder(view)
}
This is often confusing for beginners.
Remember only one thing:
onCreateViewHolder()creates one row.
It takes:
item_comment.xml
and converts it into an Android View.
This part:
LayoutInflater
.from(parent.context)
.inflate(
R.layout.item_comment,
parent,
false
)
means:
Create a View using
item_comment.xml.
Then:
return CommentViewHolder(view)
means:
Give this row to our ViewHolder.
14. onBindViewHolder()
Now add:
override fun onBindViewHolder(
holder: CommentViewHolder,
position: Int
) {
val comment = comments[position]
holder.tvName.text = comment.name
holder.tvEmail.text = comment.email
holder.tvBody.text = comment.body
}
This is the most important function for beginners.
Its job is:
Put data into the current row.
Suppose:
position = 0
Then:
val comment = comments[position]
means:
val comment = comments[0]
This gets the first comment.
Then:
holder.tvName.text = comment.name
puts the first comment’s name into the TextView.
15. Understanding Position
Imagine our list:
Index 0 → Comment 1
Index 1 → Comment 2
Index 2 → Comment 3
Index 3 → Comment 4
Index 4 → Comment 5
Remember:
Kotlin list indexing starts from
0.
Therefore:
comments[0]
is the first comment.
And:
comments[1]
is the second comment.
RecyclerView automatically provides the correct:
position
to onBindViewHolder().
16. getItemCount()
Now add:
override fun getItemCount(): Int {
return comments.size
}
This tells RecyclerView how many items are available.
If:
comments.size
is:
5
RecyclerView knows that the list contains 5 items.
So the three main Adapter functions are:
onCreateViewHolder()
↓
Create the row
onBindViewHolder()
↓
Put data into row
getItemCount()
↓
Tell RecyclerView number of items
17. Complete Adapter
The complete Adapter becomes:
class CommentAdapter(
private val comments: List<Comment>
) : RecyclerView.Adapter<CommentAdapter.CommentViewHolder>() {
class CommentViewHolder(itemView: View) :
RecyclerView.ViewHolder(itemView) {
val tvName: TextView =
itemView.findViewById(R.id.tvName)
val tvEmail: TextView =
itemView.findViewById(R.id.tvEmail)
val tvBody: TextView =
itemView.findViewById(R.id.tvBody)
}
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): CommentViewHolder {
val view = LayoutInflater
.from(parent.context)
.inflate(
R.layout.item_comment,
parent,
false
)
return CommentViewHolder(view)
}
override fun onBindViewHolder(
holder: CommentViewHolder,
position: Int
) {
val comment = comments[position]
holder.tvName.text = comment.name
holder.tvEmail.text = comment.email
holder.tvBody.text = comment.body
}
override fun getItemCount(): Int {
return comments.size
}
}
18. Set Up RecyclerView in Activity
Now move to your Activity.
Declare:
private lateinit var recyclerView: RecyclerView
Inside onCreate():
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView = findViewById(R.id.recyclerView)
recyclerView.layoutManager =
LinearLayoutManager(this)
getComments()
}
19. Why LinearLayoutManager?
RecyclerView needs to know how items should be arranged.
We use:
LinearLayoutManager(this)
which means:
Display items vertically, one below another.
Like:
Comment 1
─────────
Comment 2
─────────
Comment 3
─────────
Comment 4
─────────
Without a LayoutManager, RecyclerView doesn’t know how to arrange the rows.
20. Now Call the API
Create:
private fun getComments() {
RetrofitClient.apiService
.getComments()
.enqueue(object : Callback<List<Comment>> {
override fun onResponse(
call: Call<List<Comment>>,
response: Response<List<Comment>>
) {
if (response.isSuccessful) {
val comments = response.body()
if (comments != null) {
recyclerView.adapter =
CommentAdapter(comments)
}
}
}
override fun onFailure(
call: Call<List<Comment>>,
t: Throwable
) {
Toast.makeText(
this@MainActivity,
"Error: ${t.message}",
Toast.LENGTH_LONG
).show()
}
})
}
Now let’s understand what happens.
21. Step-by-Step API Flow
First:
RetrofitClient.apiService
Get access to our API.
Then:
.getComments()
Create the API request.
This returns:
Call<List<Comment>>
Then:
.enqueue(...)
send the request asynchronously.
22. What Happens When Server Responds?
Retrofit receives JSON:
[
{
"postId": 1,
"id": 1,
"name": "First comment",
"email": "first@gmail.com",
"body": "Hello"
},
{
"postId": 1,
"id": 2,
"name": "Second comment",
"email": "second@gmail.com",
"body": "Hi"
}
]
The converter transforms it into:
List<Comment>
Conceptually:
JSON ARRAY
↓
Retrofit Converter
↓
List<Comment>
↓
[
Comment(...),
Comment(...)
]
23. response.body()
Inside:
override fun onResponse(...)
we write:
val comments = response.body()
Now:
comments
may contain:
Comment 1
Comment 2
Comment 3
Comment 4
Comment 5
Then:
recyclerView.adapter =
CommentAdapter(comments)
means:
Give the complete list to the Adapter.
24. What Happens After Giving List to Adapter?
Suppose our list contains 5 comments.
Adapter receives:
[
Comment 1,
Comment 2,
Comment 3,
Comment 4,
Comment 5
]
Then RecyclerView asks:
How many items?
Adapter answers using:
getItemCount()
Result:
5
Then RecyclerView creates rows using:
onCreateViewHolder()
Then fills rows using:
onBindViewHolder()
25. Complete Flow
This is the complete architecture of today’s lesson:
SERVER
↓
JSON ARRAY
↓
Retrofit
↓
List<Comment>
↓
CommentAdapter
↓
RecyclerView
↓
CommentViewHolder
↓
item_comment.xml
↓
TextViews
↓
SCREEN
26. What Happens for Position 0?
RecyclerView calls:
onBindViewHolder(holder, 0)
Then:
val comment = comments[0]
gets:
First Comment
Then:
holder.tvName.text = comment.name
displays the first name.
27. What Happens for Position 1?
RecyclerView calls:
onBindViewHolder(holder, 1)
Then:
val comment = comments[1]
gets:
Second Comment
Then that comment gets displayed in another row.
This keeps repeating for all items.
28. RecyclerView in Simple English
Remember this sentence:
RecyclerView displays multiple items, Adapter connects data with RecyclerView, ViewHolder holds one row’s views, and
onBindViewHolder()puts the correct data into each row.
29. Complete MainActivity Example
class MainActivity : AppCompatActivity() {
private lateinit var recyclerView: RecyclerView
override fun onCreate(
savedInstanceState: Bundle?
) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
recyclerView =
findViewById(R.id.recyclerView)
recyclerView.layoutManager =
LinearLayoutManager(this)
getComments()
}
private fun getComments() {
RetrofitClient.apiService
.getComments()
.enqueue(object : Callback<List<Comment>> {
override fun onResponse(
call: Call<List<Comment>>,
response: Response<List<Comment>>
) {
if (response.isSuccessful) {
val comments =
response.body()
if (comments != null) {
recyclerView.adapter =
CommentAdapter(comments)
}
} else {
Toast.makeText(
this@MainActivity,
"Something went wrong",
Toast.LENGTH_SHORT
).show()
}
}
override fun onFailure(
call: Call<List<Comment>>,
t: Throwable
) {
Toast.makeText(
this@MainActivity,
"Error: ${t.message}",
Toast.LENGTH_LONG
).show()
}
})
}
}
30. Required Internet Permission
Don’t forget the internet permission.
Open:
AndroidManifest.xml
Add:
<uses-permission
android:name="android.permission.INTERNET" />
It should be above the <application> tag.
Example:
<manifest
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission
android:name="android.permission.INTERNET" />
<application>
...
</application>
</manifest>
31. Important Imports
Your Activity may require:
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
Your Adapter may require:
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
32. Beginner Exercise Before API
If students are still confused, don’t connect Retrofit immediately.
First create hardcoded data:
val comments = listOf(
Comment(
1,
1,
"Rahul",
"rahul@gmail.com",
"Learning Android"
),
Comment(
1,
2,
"Priya",
"priya@gmail.com",
"Learning RecyclerView"
),
Comment(
1,
3,
"Amit",
"amit@gmail.com",
"Learning Retrofit"
)
)
Then:
recyclerView.adapter =
CommentAdapter(comments)
Run the app.
If the three items appear, students have understood RecyclerView.
Then remove hardcoded data and replace it with:
response.body()
This makes the learning process much easier.
33. Best Classroom Explanation
Draw this on the board:
STEP 1
API gives:
[
{},
{},
{},
{},
{}
]
5 Objects
Then:
STEP 2
Retrofit converts them into:
List<Comment>
Then:
STEP 3
List<Comment>
↓
CommentAdapter
Then:
STEP 4
Adapter:
Creates Row
↓
Finds Position
↓
Gets Comment
↓
Sets Name
↓
Sets Email
↓
Sets Body
Then:
STEP 5
RecyclerView
Row 0 → Comment 1
Row 1 → Comment 2
Row 2 → Comment 3
Row 3 → Comment 4
Row 4 → Comment 5
34. The Three RecyclerView Functions Students Must Remember
Students don’t need to memorize every line initially.
Remember these three methods:
1. onCreateViewHolder()
CREATE THE ROW
2. onBindViewHolder()
PUT DATA INTO THE ROW
3. getItemCount()
HOW MANY ITEMS?
If these three concepts are clear, RecyclerView becomes much easier.
35. One Final Example
Suppose API gives:
comments.size = 5
RecyclerView asks:
How many items?
Adapter:
override fun getItemCount(): Int {
return comments.size
}
returns:
5
Then for the first row:
position = 0
Adapter gets:
comments[0]
For second:
position = 1
Adapter gets:
comments[1]
For third:
position = 2
Adapter gets:
comments[2]
And so on.
36. Final Mental Model
Do not memorize RecyclerView code.
Remember this:
API
↓
JSON Array
↓
Retrofit
↓
List<Comment>
↓
Adapter
↓
RecyclerView
↓
ViewHolder
↓
One Item Layout
↓
Screen
And remember:
Data Class
=
Shape of one object
List<Comment>
=
Multiple Comment objects
RecyclerView
=
Displays multiple rows
Adapter
=
Connects data and rows
ViewHolder
=
Holds one row's views
position
=
Which item is currently being displayed
37. Today’s Learning Outcome
After completing this tutorial, a student should be able to explain:
- Why
[ ]in JSON means multiple objects. - Why we use
List<Comment>. - Why API return type becomes
Call<List<Comment>>. - What RecyclerView does.
- Why an Adapter is required.
- What a ViewHolder is.
- What
positionmeans. - What
onCreateViewHolder()does. - What
onBindViewHolder()does. - What
getItemCount()does. - How Retrofit data reaches RecyclerView.
The key idea is:
The API gives us a list of data. Retrofit converts that data into
List<Comment>. The Adapter takes that list and displays each Comment as a separate RecyclerView row.
SoftSence Academy — Android Development Learning Series
