Retrofit2 on Android using Kotlin

Today we will consider working with Retrofit 2. For the sake of truth, it should be noted that we will work with 2 more separate libraries, namely Picasso and Spots-dialog .



I recommend this article to read only for those who have not yet worked with Retrofit 2. After all, in this article I will describe everything in as much detail as possible so that everyone understands.



0. Project creation



Everything here is as simple as possible, create a new project in Android Studio, select Empty Activity. Next, you need to select a programming language, we will have it Kotlin.



1. Add dependencies



Now we need to add all the required libraries, so we go to build.gradle and add the following:



dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    implementation 'com.squareup.picasso:picasso:2.71828'

    //noinspection GradleCompatible
    implementation 'com.android.support:design:28.0.0'
    implementation 'com.github.d-max:spots-dialog:1.1@aar'
}


As you may have noticed, here we have added all the necessary libraries, including the Spots progress dialog. You can read more about it here. Then in build.gradle (Modul: app) we have to insert this:



 compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }


It remains only to go to manifests and add permission to use the phone
  <uses-permission android:name="android.permission.INTERNET"/>




2. Adding data classes



Before moving on to the second point, we need to find out where to get the data from? Parse will be from here.



Great, then copy the content of this site and go here. Here we paste the previously copied text from our site, after we clicked on the tree, we got a list, open it, now we see that we have 8 objects. If someone does not understand, then I will show you a screen:







What you see on the right is our text that we copied, and what on the right is already processed data.



Now back to Android Studio, we create a folder, we call it Model, there we create a kotlin class and name it Movie from a regular class, we transform the data class, we just add data before the class, and replace the curly braces with parentheses, then we indicate the variables that we already have in brackets spied on the site, by the way the variables must be of nullable type.



Complete code
data class Movie(
    var name: String? = null,
    var realname: String? = null,
    var team: String? = null,
    var firstapperance: String? = null,
    var createdby: String? = null,
    var publisher: String? = null,
    var imageurl: String? = null,
    var bio: String? = null
)




If you are wondering why the folder is called Model, then I will tell you that:



Model is the logic that is associated with application data. In other words, these are POJOs, classes for working with an API, a database.



3. Creation of Client



Next, we create the Retrofit folder, and in the folder we create an object and name it RetrofitClient, then we create a retrofit variable of the Retrofit type, after that we create a function and call it getCleint (baseUrl: String) and the return type is Retrofit. In the body of the function, it is necessary to check the retrofit for null and if the retrofit is null, then we assign Retrofit to the retrofit.Builder () attach baseUrl with the baseUrl parameter, then attach the addconverterFactory method with the GsonConverterFactory.create () parameter and build using the build ( ) and return the retrofit to a non-zero type



Complete code

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object RetrofitClient {
    private var retrofit: Retrofit? = null

    fun getClient(baseUrl: String): Retrofit {
        if (retrofit == null) {
            retrofit = Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
        }
        return retrofit!!
    }
}




The Builder in Retrofit is an instance that uses the Builder interface and API to define the URL endpoint definition for HTTP operations



4. Working with Interface



Interface - needed to create abstract classes.



Create an Interface package in it we put an Interface and call it RetrofitServieces. Create a Get request in parentheses, write quotes, and in quotes indicates the branch from which we will parse the data, this is marvel. But before that, it is worth saying what a GET and POST request are

GET - requests data from a specific resource (site)

POST - sends data to the server for further processing



Ok, now you need to create a getMovieList function, which should return a Call of type MutableList, and a MutableList should be Movie type



Complete code

import com.example.retrofitmarvel.Model.Movie
import retrofit2.Call
import retrofit2.http.*

interface RetrofitServices {
    @GET("marvel")
    fun getMovieList(): Call<MutableList<Movie>>
}




5. Common



Now we have to create the Common folder, in this folder we put an object and call it Common, create a variable, call it BASE_URL and in it we must put the link from which we parse the data, but do not put the last branch, since it is from it that we receive data ... We create the retrofitServices variable, it has a get () method, we assign RetrofitClient to it, and only then we network the getClient method with the RetrofitServices :: class.java parameter to RetrofitClient



Complete code

import com.example.retrofitmarvel.Interface.RetrofitServices
import com.example.retrofitmarvel.Retrofit.RetrofitClient

object Common {
    private val BASE_URL = "https://www.simplifiedcoding.net/demos/"
    val retrofitService: RetrofitServices
        get() = RetrofitClient.getClient(BASE_URL).create(RetrofitServices::class.java)
}




6. Layout



Go to activity_main.xml and add RecyclerView there



XML

<?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/recyclerMovieList"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>




In the layout folder, create an item_layout in the root element, specify the CardView



item_layout
<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"
    app:cardElevation="8dp"
    android:layout_margin="8dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/linearLayout"
        android:layout_width="match_parent"
        android:layout_height="167dp">


        <ImageView
            android:id="@+id/image_movie"
            android:layout_width="0dp"
            android:layout_height="100dp"
            android:layout_marginEnd="8dp"
            android:layout_marginRight="8dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/txt_name"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_bias="0.238" />

        <TextView
            android:id="@+id/txt_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="192dp"
            android:layout_marginLeft="192dp"
            android:layout_marginTop="16dp"
            android:text="name"
            android:textColor="@android:color/black"
            android:textSize="20sp"
            android:textStyle="bold"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/txt_team"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="196dp"
            android:layout_marginLeft="196dp"
            android:layout_marginTop="8dp"
            android:text="team"
            android:textColor="@android:color/black"
            android:textSize="16sp"
            android:textStyle="normal"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/txt_name" />

        <TextView
            android:id="@+id/txt_createdby"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="184dp"
            android:layout_marginLeft="184dp"
            android:layout_marginTop="12dp"
            android:text="createdby"
            android:textColor="@android:color/black"
            android:textSize="18sp"
            android:textStyle="italic"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/txt_team" />


    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.cardview.widget.CardView>




7. Adapter



Now we have to create an Adapter package, in which we put a class and call it MyMovieAdapter

Adapter is responsible for retrieving data from a dataset and for creating View objects based on this data.



In the MyMovieAdapter class, we create variables that will be available only in this class private val movieList: MutableList of type Movie and specify the return type, we will have it RecyclerView.Adapter of type MyMovieAdapter.MyViewHolder



Implement methods, namely onCreateViewHolder, getItemCount and onBindViewHolder

We create the MyViewHolder class, in this class we specify the itemView: View parameter and the return type RecyclerView.ViewHolder and we put variables in the body of this class, for example:



val image: ImageView = itemView.image_movie image_movie is drawn from item_layout

and so we specify for all the remaining view items.



Let's create a bind function with a listItem: Movie parameter, here we can make our view items clickable, I think you can do that.



Next, we rewrite getItemCount () to override fun getItemCount () = movieList.size. Everything is simple here, we created a function and return movieList.size. Great, all that remains is to deal with onBindViewHolder and onCreateViewHolder



onCreateViewHolder - creates a new ViewHolder object whenever the RecyclerView needs it.



onBindViewHolder - takes a ViewHolder object and sets the necessary data for the corresponding line in the view component



Now we will analyze the case with the onCreateViewHolder return type MyViewHolder.

Create a variable itemView, assign it LayoutInflater.from (parent.context) .inflate (R.layout.item_layout, parent, false) and return MyViewHolder with the itemView parameter. Now go to onBindViewHolder in the body we create the variable listItem: Movie and assign movieList [position]. Next, we attach the bind method with the listItem parameters to holder'y. Next, we use the Picasso library.



Picasso.get (). Load (movieList [position] .imageurl) .into (holder.image). Then add holder.txt_name.text = movieList [position] .name, and this is how we do with all our view elements



Complete code

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import com.example.retrofitmarvel.Model.Movie
import com.example.retrofitmarvel.R
import com.squareup.picasso.Picasso
import kotlinx.android.synthetic.main.item_layout.view.*

class MyMovieAdapter(private val context: Context,private val movieList: MutableList<Movie>):RecyclerView.Adapter<MyMovieAdapter.MyViewHolder>() {

    class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
        val image: ImageView = itemView.image_movie
        val txt_name: TextView = itemView.txt_name
        val txt_team: TextView = itemView.txt_team
        val txt_createdby: TextView = itemView.txt_createdby

        fun bind(listItem: Movie) {
            image.setOnClickListener {
                Toast.makeText(it.context, "  ${itemView.image_movie}", Toast.LENGTH_SHORT)
                    .show()
            }
            itemView.setOnClickListener {
                Toast.makeText(it.context, "  ${itemView.txt_name.text}", Toast.LENGTH_SHORT).show()
            }
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
        return MyViewHolder(itemView)
    }

    override fun getItemCount() = movieList.size

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val listItem = movieList[position]
        holder.bind(listItem)

        Picasso.get().load(movieList[position].imageurl).into(holder.image)
        holder.txt_name.text = movieList[position].name
        holder.txt_team.text = movieList[position].team
        holder.txt_createdby.text = movieList[position].createdby
    }

}





8. MainActivity



Excellent! Left just a little bit. Go to MainActivity to begin with, we will create variables, and in order not to declare them of type null, we will declare them through lateinit var mService: RetrofitServices, we need to create 3 more of these, namely: LinearLayoutManager, MyMovieAdapter, AlertDialog. You can call them whatever you want, it doesn't matter. In the onCreate method, we assign Common.retrofitServices to RetrofitServices. On the next line, we attach setHasFixedSize (true) to our recyclerView - thanks to this method we can optimize our list, then we assign LinearLayoutManager (this) to our layoutManager.



Layout ManagerIs the thing that is responsible for the positioning of the View components that are no longer visible to the user. Further, everything is also easy to attach the layoutManager to our list and already assign the layoutManager to this. Ok, now working with the SpotsDialog library. we specify the previously named variable with the AlertDialog type we assign SpotsDialog we attach the Builder method after that we attach the setCancelablec method with the true parameter to this we must attach the setContext method with the this parameter and attach the build method.



Now we have to create a new function outside of the onCreate method called the function getAllMovieList. In the body of this function, we must specify our dialog and attach the show () method to it,

then add the getMovieList method to mService .enqueue (object: Callback <MutableList> {)



Now we need to implement the methods, we have two of them onResponse and onFailure

and in onResponse, namely in the body of this method we assign to adapter'y



MyMovieAdapter(baseContext, response.body() as MutableList<Movie>)


further to adapter'y we assign the notifyDataSetChanged () method. To our list, we attach adapter and assign adapter. then we assign the dismiss () method to dialog. This means that our dialog will disappear after our data is loaded.



Complete code

import android.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.retrofitmarvel.Adapter.MyMovieAdapter
import com.example.retrofitmarvel.Common.Common
import com.example.retrofitmarvel.Interface.RetrofitServices
import com.example.retrofitmarvel.Model.Movie
import com.example.retrofitmarvel.R

import dmax.dialog.SpotsDialog
import kotlinx.android.synthetic.main.activity_main.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class MainActivity : AppCompatActivity() {

    lateinit var mService: RetrofitServices
    lateinit var layoutManager: LinearLayoutManager
    lateinit var adapter: MyMovieAdapter
    lateinit var dialog: AlertDialog

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mService = Common.retrofitService
        recyclerMovieList.setHasFixedSize(true)
        layoutManager = LinearLayoutManager(this)
        recyclerMovieList.layoutManager = layoutManager
        dialog = SpotsDialog.Builder().setCancelable(true).setContext(this).build()

        getAllMovieList()
    }

    private fun getAllMovieList() {
        dialog.show()
        mService.getMovieList().enqueue(object : Callback<MutableList<Movie>> {
            override fun onFailure(call: Call<MutableList<Movie>>, t: Throwable) {

            }

            override fun onResponse(call: Call<MutableList<Movie>>, response: Response<MutableList<Movie>>) {
                adapter = MyMovieAdapter(baseContext, response.body() as MutableList<Movie>)
                adapter.notifyDataSetChanged()
                recyclerMovieList.adapter = adapter

                dialog.dismiss()
            }
        })
    }
}




Excellent! In this article, we learned how to work with Retrofit2 and put it in a RecyclerView.



All Articles