How to Implement Paging 3 Library in Android Kotlin?
Introduction
Android introduced “Pagination” just because most applications have a large amount of data and display that data in the listing. However, users generally consider only small parts of data listing. Just fetching all data from the network and displaying it in listview or recycler view is not an optimum solution.
The paging library is the easiest way to display data in a listing format. Currently, Paging 3 Library is available with an alpha release, whereas if you want to work with a stable version of Paging Library then Paging 2 is available without Kotlin coroutine flow.
This demographic workflow will be about on Paging 3 Library in Android
Features of Paging 3
Paging 3 library comes with the latest features that might cover all the scenarios of listing the data in any format like (Listview, Recyclerview, GridView etc ..)
- Support live data
- Paging 3 library support coroutine flow
- List separators (Decoration with Item )
Implement Paging 3 Library in Android Kotlin
The below process will guide you that how to implement Paging 3 in your
application.
Step 1: Implementation of Library into Project:
You have to implement this library in the Build.gradle folder.
implementation "androidx.paging:paging-runtime-ktx:3.1.0-beta01"
Step 2: Generating Data Models
The data resource class helps you to get data and retrieve data from the network. This means the network will provide some data and we have to store it in some class which is called data class.
PassengerListResponse
@Parcelize
data class PassengerListResponse(
@SerializedName("totalPassengers") val totalPassengers: Int,
@SerializedName("totalPages") val totalPages: Int,
@SerializedName("data") val passengerList: List<Passenger>
) : Parcelable
@Parcelize
data class Passenger(
@SerializedName("_id") val id: String,
@SerializedName("name") val name: String,
@SerializedName("trips") val trips: Int,
@SerializedName("__v") val visits: Int
) : Parcelable
As You see PassengerListResponse class, we have several elements that will be available on the network side with the name of the data.
Step 3: Integrate Source File
We need to create a Datasource class which help you to retrieve data from the services and it looks like as mentioned below :
class PassengerItemDataSource(
private val dataRepository: DataRepository
) : PagingSource<Int, Passenger>() {
companion object {
private const val STARTING_PAGE_INDEX = 1
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Passenger> {
val position = params.key ?: STARTING_PAGE_INDEX
//Retrofit will automatically make suspend functions main-safe so you can call them directly from Dispatchers.Main
return when (val result = dataRepository.getPassengerListForPage(position, params.loadSize)) {
is Resource.Failure -> LoadResult.Error(Exception(result.message))
is Resource.Loading -> LoadResult.Error(Exception())
is Resource.Success -> {
LoadResult.Page(
data = result.data.passengerList,
prevKey = if (position == STARTING_PAGE_INDEX) null else position - 1,
nextKey = if (result.data.passengerList.isEmpty()) null else position + 1
)
}
}
}
override fun getRefreshKey(state: PagingState<Int, Passenger>): Int? {
// We need to get the previous key (or next key if previous is null) of the page
// that was closest to the most recently accessed index.
// Anchor position is the most recently accessed index
return state.anchorPosition?.let { anchorPosition ->
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
}
}
}
Step 4: Develop Data Source Factory
After creating the Datasource class, you need to implement Data Source Factory.
The data source Factory class is used to create data sources from the network.
class PassengerDataSourceFactory(private val dataRepository: DataRepository)
{
fun getPassengers()=Pager(
config = PagingConfig(
pageSize = NETWORK_PAGE_SIZE,
enablePlaceholders = false
),
pagingSourceFactory = { PassengerItemDataSource(dataRepository) }
).liveData
}
After creating the Data sources factory, we need to create one adapter class where we can display the list of data.
Step 5: Create List Adapter Class
The list adapter class provides the default implementation of listener interfaces. If you inherit the adapter class, you will not be provided with the implementation of all the methods of listener interfaces so it saves codes.
PasangerListAdapter:
class PassengerListAdapter :
PagingDataAdapter<Passenger, PassengerListAdapter.PassengerViewHolder>(PassengerListDiffCallback()) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): PassengerViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = RawPassengerItemBinding.inflate(inflater)
return PassengerViewHolder(binding)
}
override fun onBindViewHolder(
holder: PassengerViewHolder,
position: Int
) {
val item: Passenger? = getItem(position)
item?.let {
holder.bind(item)
}
}
override fun onBindViewHolder(
holder: PassengerViewHolder,
position: Int,
payloads: MutableList<Any>
) {
if (payloads.isNullOrEmpty()) {
super.onBindViewHolder(holder, position, payloads)
} else {
val newItem = payloads[0] as Passenger
holder.bind(newItem)
}
}
inner class PassengerViewHolder(private val rawPassengerItemBinding: RawPassengerItemBinding) :
RecyclerView.ViewHolder(rawPassengerItemBinding.root) {
fun bind(passenger: Passenger) {
rawPassengerItemBinding.passenger = passenger
}
}
class PassengerListDiffCallback : DiffUtil.ItemCallback<Passenger>() {
override fun areItemsTheSame(oldItem: Passenger, newItem: Passenger) =
oldItem.id == newItem.id
override fun areContentsTheSame(
oldItem: Passenger,
newItem: Passenger
) = oldItem == newItem
override fun getChangePayload(oldItem: Passenger, newItem: Passenger): Any? {
if (oldItem != newItem) {
return newItem
}
return super.getChangePayload(oldItem, newItem)
}
}
}
In this adapter class, we have some override method which is used to set our raw items.
Override Method Of Adapter:
1. OnCreateViewHolder()
2. onBindViewHolder()
3. And ViewHolder Class ()
The below code describes how to create an adapter class.
class PassengerListAdapter :
PagingDataAdapter<Passenger, PassengerListAdapter.PassengerViewHolder>(PassengerListDiffCallback()) {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): PassengerViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = RawPassengerItemBinding.inflate(inflater)
return PassengerViewHolder(binding)
}
override fun onBindViewHolder(
holder: PassengerViewHolder,
position: Int
) {
val item: Passenger? = getItem(position)
item?.let {
holder.bind(item)
}
}
override fun onBindViewHolder(
holder: PassengerViewHolder,
position: Int,
payloads: MutableList<Any>
) {
if (payloads.isNullOrEmpty()) {
super.onBindViewHolder(holder, position, payloads)
} else {
val newItem = payloads[0] as Passenger
holder.bind(newItem)
}
}
inner class PassengerViewHolder(private val rawPassengerItemBinding: RawPassengerItemBinding) :
RecyclerView.ViewHolder(rawPassengerItemBinding.root) {
fun bind(passenger: Passenger) {
rawPassengerItemBinding.passenger = passenger
}
}
class PassengerListDiffCallback : DiffUtil.ItemCallback<Passenger>() {
override fun areItemsTheSame(oldItem: Passenger, newItem: Passenger) =
oldItem.id == newItem.id
override fun areContentsTheSame(
oldItem: Passenger,
newItem: Passenger
) = oldItem == newItem
override fun getChangePayload(oldItem: Passenger, newItem: Passenger): Any? {
if (oldItem != newItem) {
return newItem
}
return super.getChangePayload(oldItem, newItem)
}
}
}
Step 6: Loading Adapter in fragment or Activity
This code is Used how to access pagination in our activity or fragment
passengerListAdapter.addLoadStateListener { loadState ->
when (val state = loadState.source.refresh) {
is LoadState.NotLoading -> {
/**
* Setting up the views as per your requirement
*/
}
is LoadState.Loading -> {
/**
* Setting up the views as per your requirement
*/
}
is LoadState.Error -> {
/**
* Setting up the views as per your requirement
*/
Youractivity?.showMessage(state.error.message.orEmpty())
}
}
}
Example of Pagination
Conclusion
To conclude, Pagination 3 is widely used in most applications where there is a list of data available in the android application. Implementation of pagination provides an efficient user experience. Paging 3 also enables developers to implement pagination with ease and better accuracy.
We hope you have enjoyed reading this article and found it helpful. Good Luck with your implementation of Pagination using the Paging 3 library in Android.
FAQ
1. Why do we use the paging 3 library for pagination?
We can use the paging 3 library for pagination. Because it provides first-class Kotlin support to handle easy or complex data operations like data loading from network, database or combination of different data sources. And the paging 3 library can also support Kotlin coroutine flow.
2. Can we use the Paging 3 Library in Java?
Yes, We can use the paging 3 Library in java as well.