In this android app tutorial, I’ll explain, RecyclerView Kotlin implementation in a step by step. In this sample application, we’ll use Kotlin programming language along with RxAndroid and Dagger2. So let’s get started.
What is RecyclerView
If you are an android developer you already familiar RecyclerView. When you need to show scrolling list based on large data set, you should use RecyclerView.
RecyclerView is a flexible and upgraded version of ListView. In the RecyclerView several different works together to display your data. Basically, RecyclerView works on ViewHolder design pattern. Each row managed by ViewHolder. So you can say, each view holder is in responsible for displaying a single item with a view.
Technology Used
In this RecyclerView Kotlin tutorial, I’ll create a sample application that fetch the users details from server and will displayed on RecyclerView. In this sample application, We’ll use following technology
- Androidx Support Lib
- Retrofit for APIs calling
- RxJava
- Glide
1. Create a RecyclerView Sample App
Let’s move to Android Studio, create a new project with androidx and choose Kotlin language from language dropdown.
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' android { compileSdkVersion 29 buildToolsVersion "29.0.2" defaultConfig { applicationId "com.recyclerviewexample" minSdkVersion 15 targetSdkVersion 29 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } def lifeCycleExtensionsVersion = '1.1.1' def retrofitVersion = '2.3.0' def daggerVersion = '2.13' def glideVersion = '4.9.0' def rxJavaVersion = '2.1.1' dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.core:core-ktx:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion" implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion" implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion" implementation "io.reactivex.rxjava2:rxandroid:$rxJavaVersion" implementation "com.google.dagger:dagger:$daggerVersion" implementation "com.google.dagger:dagger-android-support:$daggerVersion" kapt "com.google.dagger:dagger-compiler:$daggerVersion" kapt "com.google.dagger:dagger-android-processor:$daggerVersion" implementation "android.arch.lifecycle:extensions:$lifeCycleExtensionsVersion" implementation "com.github.bumptech.glide:glide:$glideVersion" testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation 'androidx.recyclerview:recyclerview:1.1.0-beta04' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0-alpha02' }
After adding these dependencies, Sync the project.
2. Open the MainActvitiy layout file and paste below code.
In this layout file, I’m adding the following widget.
- RecyclerView for showing user list.
- AppCompatTextView for showing an error message.
- ProgressBar for showing loader while getting data from a remote server.
- SwipeRefreshLayout as a root view for refreshing list item on pull to refresh.
<?xml version="1.0" encoding="utf-8"?> <androidx.swiperefreshlayout.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/swipeRefreshLayout" tools:context=".view.MainActivity"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.recyclerview.widget.RecyclerView android:id="@+id/usersList" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp" android:layout_marginRight="8dp" app:layout_constraintStart_toStartOf="parent" android:layout_marginLeft="8dp" android:layout_marginStart="8dp" android:layout_marginBottom="8dp" app:layout_constraintBottom_toBottomOf="parent" android:layout_marginTop="8dp" app:layout_constraintTop_toTopOf="parent"/> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/list_error" android:layout_width="0dp" android:layout_height="0dp" android:text="@string/error_text" android:gravity="center" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp" android:layout_marginRight="8dp" android:layout_marginBottom="8dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" android:layout_marginLeft="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" app:layout_constraintTop_toTopOf="parent"/> <ProgressBar android:id="@+id/loading_view" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp" android:layout_marginRight="8dp" android:layout_marginBottom="8dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" android:layout_marginLeft="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" app:layout_constraintTop_toTopOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
2. Create a data model class for below JSON
The JSON payload looks like below. I need to write a data model to parse that JSON.
{ "page": 1, "per_page": 6, "total": 12, "total_pages": 2, "data": [ { "id": 1, "email": "george.bluth@reqres.in", "first_name": "George", "last_name": "Bluth", "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg" }, . . ] }
2.1 For parsing above JSON I’m creating below data model class named is Data.kt
package com.recyclerviewexample.model import com.google.gson.annotations.SerializedName data class Data( @SerializedName("page") val page: Int, @SerializedName("per_page") val perPage: Int, @SerializedName("total") val total: Int, @SerializedName("total_pages") val totalPages: Int, @SerializedName("data") val users: List<User> )
2.2 Create another data model class named is User.kt
package com.recyclerviewexample.model import com.google.gson.annotations.SerializedName data class User( @SerializedName("avatar") val avatar: String, @SerializedName("email") val email: String, @SerializedName("first_name") val firstName: String, @SerializedName("id") val id: Int, @SerializedName("last_name") val lastName: String )
3. Prepare an XML layout for showing list user list item.
For displaying the user on a list item in RecyclerView, creates layout named is item_user.xml. It contains ImageView for use image and TextViews for the display name and email.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="@dimen/layout_height" android:layout_margin="4dp"> <ImageView android:id="@+id/imageView" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:padding="@dimen/standard_padding" /> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="2" android:gravity="center_vertical" android:orientation="vertical"> <TextView android:id="@+id/name" style="@style/Title" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:text="Name" /> <TextView android:id="@+id/email" style="@style/Text" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:text="Email" /> </LinearLayout> </LinearLayout>
4. Create a RecyclerView Adapter that holds that holds user item.
For the above layout creates a RecycleView holder adapter that holds the user list item
package com.recyclerviewexample.view import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.recyclerviewexample.R import com.recyclerviewexample.model.User import com.recyclerviewexample.util.loadImage import kotlinx.android.synthetic.main.item_user.view.* class UserListAdapter(var users: ArrayList<User>) : RecyclerView.Adapter<UserListAdapter.UserViewHolder>() { fun updateUsers(newUsers: List<User>) { users.clear() users.addAll(newUsers) notifyDataSetChanged() } override fun onCreateViewHolder(parent: ViewGroup, p1: Int) = UserViewHolder( LayoutInflater.from(parent.context).inflate(R.layout.item_user, parent, false) ) override fun getItemCount() = users.size override fun onBindViewHolder(holder: UserViewHolder, position: Int) { holder.bind(users[position]) } class UserViewHolder(view: View) : RecyclerView.ViewHolder(view) { private val imageView = view.imageView private val userName = view.name private val userEmail = view.email fun bind(country: User) { userName.text = country.firstName + " " + country.lastName userEmail.text = country.email imageView.loadImage(country.avatar) } } }
5. Create a Util class for with help us to display user profile picture
package com.recyclerviewexample.util import android.widget.ImageView import com.bumptech.glide.Glide import com.bumptech.glide.request.RequestOptions import com.recyclerviewexample.R fun ImageView.loadImage(uri: String?) { val options = RequestOptions() .placeholder(R.drawable.loader) .circleCrop() .error(R.mipmap.ic_launcher_round) Glide.with(this.context) .setDefaultRequestOptions(options) .load(uri) .into(this) }
6. Step up the Retrofit client for calling APIs
In src folder create a new Retrofit calling interface named is UsersApi
package com.recyclerviewexample.model import io.reactivex.Single import retrofit2.http.GET interface UsersApi { @GET("users") fun getUsers(): Single<Data> }
7. Now create Dagger module file name is ApiModule
In this dagger module, we are providing Retrofit client and User Service using @provide annotation
package com.recyclerviewexample.di import com.recyclerviewexample.model.UsersApi import com.recyclerviewexample.model.UsersService import dagger.Module import dagger.Provides import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory @Module class ApiModule { private val BASE_URL = "https://reqres.in/api/" @Provides fun provideUsersApi(): UsersApi { return Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build() .create(UsersApi::class.java) } @Provides fun provideUsersService(): UsersService { return UsersService() } }
8. Let prepare to dagger component named is ApiComponent
In this ApiComponent interface, we are injecting User Service and ListView Model that I’ll create later.
package com.recyclerviewexample.di import com.recyclerviewexample.model.UsersService import com.recyclerviewexample.viewmodel.ListViewModel import dagger.Component @Component(modules = [ApiModule::class]) interface ApiComponent { fun inject(service: UsersService) fun inject(viewModel: ListViewModel) }
9. Now I’m creating a service provider class for calling named is UsersService.
package com.recyclerviewexample.model import com.recyclerviewexample.di.DaggerApiComponent import io.reactivex.Single import javax.inject.Inject class UsersService { @Inject lateinit var api: UsersApi init { DaggerApiComponent.create().inject(this) } fun getUsers(): Single<Data> { return api.getUsers() } }
10. Finally, create a subclass of ViewModel name is ListViewModel
Create a class named is ListViewModel, which extends the ViewModel. In this class, we using fetching user list from a remote server and updating ViewModel value.
package com.recyclerviewexample.viewmodel import android.util.Log import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.recyclerviewexample.di.DaggerApiComponent import com.recyclerviewexample.model.Data import com.recyclerviewexample.model.User import com.recyclerviewexample.model.UsersService import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.observers.DisposableSingleObserver import io.reactivex.schedulers.Schedulers import javax.inject.Inject class ListViewModel : ViewModel() { @Inject lateinit var usersService: UsersService init { DaggerApiComponent.create().inject(this) } private val disposable = CompositeDisposable() val users = MutableLiveData<List<User>>() val userLoadError = MutableLiveData<Boolean>() val loading = MutableLiveData<Boolean>() fun refresh() { fetchUsers() } private fun fetchUsers() { loading.value = true disposable.add( usersService.getUsers() .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(object : DisposableSingleObserver<Data>() { override fun onSuccess(data: Data) { Log.d("error ", "" + data) users.value = data.users userLoadError.value = false loading.value = false } override fun onError(e: Throwable) { userLoadError.value = true loading.value = false Log.d("error ", "" + e.printStackTrace()) } }) ) } override fun onCleared() { super.onCleared() disposable.clear() } }
11. Open the MainActvitiy and paste below code
Basically, In this class, I observing ViewModel and based on changes. I update the adapter list. Apart from this, I refresh value on pull to refresh using SwipeRefreshLayout component.
package com.recyclerviewexample.view import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.LinearLayoutManager import com.recyclerviewexample.R import com.recyclerviewexample.viewmodel.ListViewModel import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { lateinit var viewModel: ListViewModel private val usersAdapter = UserListAdapter(arrayListOf()) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) viewModel = ViewModelProviders.of(this).get(ListViewModel::class.java) viewModel.refresh() usersList.apply { layoutManager = LinearLayoutManager(context) adapter = usersAdapter } swipeRefreshLayout.setOnRefreshListener { swipeRefreshLayout.isRefreshing = false viewModel.refresh() } observeViewModel() } fun observeViewModel() { viewModel.users.observe(this, Observer { countries -> countries?.let { usersList.visibility = View.VISIBLE usersAdapter.updateUsers(it) } }) viewModel.userLoadError.observe(this, Observer { isError -> isError?.let { list_error.visibility = if (it) View.VISIBLE else View.GONE } }) viewModel.loading.observe(this, Observer { isLoading -> isLoading?.let { loading_view.visibility = if (it) View.VISIBLE else View.GONE if (it) { list_error.visibility = View.GONE usersList.visibility = View.GONE } } }) } }
Conclusion
That’s all, about RecyclerView in Kotlin, So guys in this android article, we have learned android Kotlin RecyclerView implementation. I hope it’s helpful for you, then help me by sharing this post on your social media profile such as facebook, Twitter, and Linkedin. I would like to suggest please go threw my other article for working with LiveData and top of it for background jobs you can use coroutines.
If you have any queries please put your comment below.
Android RecyclerView Example in Java
12 Comments
great tutorial would be cool to download code do..missing dimension and style..
Check your code, this is garbage. create a git repo for your code
Great tutorial. Could you post the dimension.xml and styles.xml snippets as they are referenced in the item_user.xml file.
sure will update soon
Thanks
Any update on the update? 🤔
where is class com.recyclerviewexample.di.DaggerApiComponent
This is an autogenerated file, that generated by Dagger2 itself. Once youwritten everything, just rebuild your project.
got it
DaggerApiComponent not find please help
clear and remove import than rebuild. I hope this will help you
Thanks