Create a recyclerview with image from API in Kotlin Android:
In our last post, we have learned how to create one basic recyclerview and how to load data in it. In this post, we will move one more step and create one recyclerview with images and text.
I have deployed one API that we can use to load data to the recyclerview.
In short,
- The app will hit the API and get all data in JSON format.
- Each element in the JSON includes links for an image, text for title, and text for description to show.
- We will load the data in each cell of the recyclerview.
How to do that in Android:
If you think it like a developer:
- Fetch the data using the API
- Parse the data from the JSON response.
- Load the data in the recyclerview.
For these, we need the below two libraries:
- Networking library to fetch the data using the API
- Parser to parse the JSON data.
- Library to load the images
For networking, we will use one library called Retrofit and for parsing the JSON response, we will use one parser provided by retrofit called Moshi.
Below is the API endpoint to fetch the data that we are loading :
http://simple-node-app-nkd.herokuapp.com/
If you open it in a browser, you will see the response. It is an array of objects with each object containing id, title, description, and image keys. image key holds the url for the image to load.
Android Project structure:
For this project, We need :
- One xml view to show each list item in the recyclerview and xml file for the main activity.
- One network class to make a GET request to the API.
- One model class that will define the structure of the items we will receive from the API.
- One Adapter class to load the data in the recyclerview
- Main Activity class.
Let’s create them one by one.
Android Project:
We have one MainActivity as the parent activity and activity_main.xml as its xml file.
Gradle changes:
In your application level build.gradle file, add the below lines:
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.moshi:moshi-kotlin:1.11.0'
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
implementation 'com.github.bumptech.glide:glide:4.11.0'
We have added dependencies for recyclerview, cardview, retrofit, moshi and glide. The versions might be different for you.
Internet permission:
We are making API calls, so we need to add permission for internet in the manifest file. Open your AndroidManifest.xml file and add the below line inside manifest tags:
<uses-permission android:name="android.permission.INTERNET"/>
Views:
Change the activity_main.xml file as below:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="1dp"
android:layout_marginLeft="1dp"
android:layout_marginTop="25dp"
android:layout_marginEnd="1dp"
android:layout_marginRight="1dp"
android:layout_marginBottom="1dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
We added one recycler view in a ConstraintLayout.
Similarly, create one list_item.xml and add the below code:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView android:layout_height="wrap_content"
android:layout_width="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardCornerRadius="8dp"
app:cardElevation="8dp"
android:layout_margin="5dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintWidth_percent=".3"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:src="@drawable/ic_launcher_background"
/>
<TextView
android:id="@+id/tvTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@+id/imageView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:textSize="25sp"
android:fontFamily="sans-serif"
android:textColor="#212121"
android:layout_marginStart="10dp"
android:text="This is a very long title and here it is"
android:layout_marginLeft="10dp" />
<TextView
android:id="@+id/tvDescription"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="@+id/tvTitle"
app:layout_constraintEnd_toEndOf="@id/tvTitle"
app:layout_constraintTop_toBottomOf="@+id/tvTitle"
app:layout_constraintBottom_toBottomOf="parent"
android:textSize="18sp"
android:fontFamily="sans-serif"
android:text="This is a very long title and here it is"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
This xml file is used for each item in the recyclerview. If you open this in the Design tab in Android Studio, it will look as like below :
Model class:
Create one new file Property and add the below code:
data class Property(val id: Int, val title: String, val description: String, val image: String)
This is a data class and it is used to hold the items of the JSON response.
Api service:
Create one class called ApiService.kt with the below code:
import com.example.myapplication.models.Property
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
private const val BASE_URL = "http://simple-node-app-nkd.herokuapp.com"
private val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
private val retrofit = Retrofit.Builder().addConverterFactory(MoshiConverterFactory.create(moshi)).baseUrl(BASE_URL).build()
interface ApiService{
@GET(".")
fun getAllData(): Call<List<Property>>
}
object Api {
val retrofitService: ApiService by lazy{retrofit.create(ApiService::class.java)}
}
We are creating one Retrofit object with moshi as the converter factory retrofit. The ApiService interface contains all API endpoints that we will use.
Adapter class:
Create one class MyAdapter.kt with the below code:
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.example.myapplication.models.Property
import org.w3c.dom.Text
class MyAdapter(private val data: List<Property>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
class MyViewHolder(val view: View): RecyclerView.ViewHolder(view){
fun bind(property: Property){
val title = view.findViewById<TextView>(R.id.tvTitle)
val imageView = view.findViewById<ImageView>(R.id.imageView)
val description = view.findViewById<TextView>(R.id.tvDescription)
title.text = property.title
description.text = property.description
Glide.with(view.context).load(property.image).centerCrop().into(imageView)
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.list_item, parent, false)
return MyViewHolder(v)
}
override fun getItemCount(): Int {
return data.size
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(data[position])
}
}
This class uses Glide to load the image of Property to the imageview defined in the list_item.xml layout. Here,
- onCreateViewHolder loads the layout for the list items.
- getItemCount returns the total number of items to show in the recyclerview
- bind method takes one Property object and loads its content in the list item.
MainActivity:
Below is the code for our MainActivity :
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.myapplication.models.Property
import com.example.myapplication.network.Api
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
class MainActivity : AppCompatActivity(){
private lateinit var recyclerView: RecyclerView
private lateinit var manager: RecyclerView.LayoutManager
private lateinit var myAdapter: RecyclerView.Adapter<*>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
manager = LinearLayoutManager(this)
getAllData()
}
fun getAllData(){
Api.retrofitService.getAllData().enqueue(object: Callback<List<Property>>{
override fun onResponse(
call: Call<List<Property>>,
response: Response<List<Property>>
) {
if(response.isSuccessful){
recyclerView = findViewById<RecyclerView>(R.id.recycler_view).apply{
myAdapter = MyAdapter(response.body()!!)
layoutManager = manager
adapter = myAdapter
}
}
}
override fun onFailure(call: Call<List<Property>>, t: Throwable) {
t.printStackTrace()
}
})
}
}
Here, we are calling the getAllData method to load all data we received from the API using retrofit. Once it loads all data, onResponse will be called. Inside this method, we are checking if the response is successful or not. If it is successful, we are creating one adapter with the items we received and adding them to the recyclerview.
That’s it, if you run this app now, it will look as like below:
You might also like:
- Android Kotlin program to load image from url using glide
- Android Kotlin example to pass data from one Activity to another
- How to create a basic recyclerview in Android using Kotlin
- How to validate an email in Kotlin in Android
- How to hide soft keyboard in Android (Kotlin) programmatically
- How to change the theme of Android Studio