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)
}
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 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.
If you have any queries please put your comment below.
Android RecyclerView Example in Java