Android RecyclerView with Retrofit – Load JSON Array into RecyclerView Step by Step

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:

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 position means.
  • 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

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top