Welcome to implementing pagination using paging library. In this post, I’m going to discuss what is the paging library used for? How does it work? what are the advantages? and will learn implementation step of android paging library. So let’s get started
Introduction
What is paging and What is need of paging library. As you see in figure on right side represents a RecyclerView. Paging does, meaning the loading data for list is piecemeal. Basically, loading 3 items at a time in this image. We could load 10 at a time or 50 at a time. If you load all 1000 items once it will be very slow and smash the user experience.
Android paging library makes it easy to load data gradually and gracefully within our app’s RecyclerView. It helps in loading and displaying data efficiently in chunks.

In this diagram, File have to be data source and from the data source you can do anythings such as call web API or database call, we can do anythings to get data. From that we get the PagedList after that creates page list adapter, which than populate our RecyclerView
Advantages of Paging Library
- Paging library data request consumes less network bandwidth and fewer system resources. So the user needed a small data plan will appreciate a data conscious app .
- App gives quick response even during the data update and refresh and also continue to respond quickly to the user input.
Technology Used
Following technology, we are using in this android app tutorials. Prerequisites are, you have to knowledge of below android component.
- Android Data Binding – allows us to bind the UI components in the layout to data resources
- ViewModel – It can hold value belong to the activity.
- LiveData – is a lifecycle-aware data holder with the observer pattern.
- Retrofit – Retrofit is mostly used for library networking in Android
- Android Paging Library – I will explain in this tutorial
- Android Studio 3.0 or later – You need minimum Android Studio version 3.0 or later
Step to implement Android Paging Library
- Project Creation & Set Up
- Create Models
- Set Up Retrofit Client
- Create a Data Source
- Create a DataSource Factory
- Setup ViewModel
- Create a PagedListAdapter
- Add Paging Stuff in MainActivity
- Conclusion
1. Project Creation & Set Up
Let’s move to Android Studio and create a Java/Kotlin android project. After project creation, You have to add certain dependencies for this android app tutorial.
1.1 Let’s open the app level gradle file and add below dependencies
Using Paging, first of all, we need to add paging library dependency in gradle. Apart from this you need to some more dependencies such as LiveData, Retrofit etc.
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-kapt' android { compileSdkVersion 29 buildToolsVersion "29.0.0" defaultConfig { applicationId "com.example.paginglibrary" 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 paging_version = "1.0.1" def glide_version = "4.9.0" def constraint_version = "1.1.3" def retrofit_version = "2.4.0" def appcompat_version = "1.1.0" dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.core:core-ktx:1.1.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "androidx.appcompat:appcompat:$appcompat_version" implementation "androidx.constraintlayout:constraintlayout:$constraint_version" implementation "com.github.bumptech.glide:glide:$glide_version" // paging library implementation "android.arch.paging:runtime:$paging_version" // retrofit implementation "com.squareup.retrofit2:retrofit:$retrofit_version" implementation 'com.squareup.retrofit2:converter-gson:2.4.0' // Logging implementation 'com.squareup.okhttp3:logging-interceptor:3.14.2' implementation 'androidx.recyclerview:recyclerview:1.1.0-beta04' implementation 'androidx.cardview:cardview:1.0.0' implementation 'com.google.android.material:material:1.1.0-alpha10' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' }
apply plugin: 'com.android.application' android { compileSdkVersion 29 buildToolsVersion "29.0.0" defaultConfig { applicationId "com.example.paginglibrary" 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' } } compileOptions { sourceCompatibility 1.8 targetCompatibility 1.8 } } def paging_version = "1.0.1" def glide_version = "4.9.0" def constraint_version = "1.1.3" def retrofit_version = "2.4.0" def appcompat_version = "1.1.0" dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation "androidx.appcompat:appcompat:$appcompat_version" implementation "androidx.constraintlayout:constraintlayout:$constraint_version" implementation "com.github.bumptech.glide:glide:$glide_version" // paging library implementation "android.arch.paging:runtime:$paging_version" // retrofit implementation "com.squareup.retrofit2:retrofit:$retrofit_version" implementation 'com.squareup.retrofit2:converter-gson:2.4.0' // Logging implementation 'com.squareup.okhttp3:logging-interceptor:3.14.2' implementation 'androidx.recyclerview:recyclerview:1.1.0-beta04' implementation 'androidx.cardview:cardview:1.0.0' implementation 'com.google.android.material:material:1.1.0-alpha10' testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' }
1.2 Add Internet Permission
In this android app tutorial we fetch data from server, so let’s add INTERNET uses permission in AndroidManifest.
<uses-permission android:name="android.permission.INTERNET"/>
2. Let’s create a model class
In this tutorial, we get data from server. The JSON model is below
{ "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" }, { "id": 2, "email": "janet.weaver@reqres.in", "first_name": "Janet", "last_name": "Weaver", "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg" } ] }
2. Let’s create a model class for parsing JSON payload
I’m creating a models class one for upper JSON object second for inner data JSON object. Let’s creates a class for an upper model class named is UserResponse.
package com.example.paginglibrary.model import com.google.gson.annotations.SerializedName class User { @field:SerializedName("avatar") var avatar: String? = null @field:SerializedName("email") var email: String? = null @field:SerializedName("first_name") var firstName: String? = null @field:SerializedName("id") var id: Long? = null @field:SerializedName("last_name") var lastName: String? = null } class UserResponse { @field:SerializedName("data") var users: List<User>? = null @field:SerializedName("page") var page: Int = 0 @field:SerializedName("per_page") var perPage: Long = 0 @field:SerializedName("total") var total: Long = 0 @field:SerializedName("total_pages") var totalPages: Int = 0 }
package com.example.paginglibrary.model; import com.google.gson.annotations.SerializedName; import java.util.List; public class UserResponse { @SerializedName("data") private List<User> users; @SerializedName("page") private Long page; @SerializedName("per_page") private Long perPage; @SerializedName("total") private Long total; @SerializedName("total_pages") private Long totalPages; public List<User> getUsers() { return users; } public void setUsers(List<User> users) { this.users = users; } public Long getPage() { return page; } public void setPage(Long page) { this.page = page; } public Long getPerPage() { return perPage; } public void setPerPage(Long perPage) { this.perPage = perPage; } public Long getTotal() { return total; } public void setTotal(Long total) { this.total = total; } public Long getTotalPages() { return totalPages; } public void setTotalPages(Long totalPages) { this.totalPages = totalPages; } } // User class package com.example.paginglibrary.model; import com.google.gson.annotations.SerializedName; public class User { @SerializedName("avatar") private String avatar; @SerializedName("email") private String email; @SerializedName("first_name") private String firstName; @SerializedName("id") private Long id; @SerializedName("last_name") private String lastName; public String getAvatar() { return avatar; } public void setAvatar(String avatar) { this.avatar = avatar; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } }
3. Set Up Retrofit Client
As you know retrofit is most commonly used network services in android. We have to set up retrofit client for this app also. Let’s go ahead
3.1 Retrofit Interface
This should be pretty self-explanatory. We are creating service interface named is RestApiService and expose get request methods.
package com.example.paginglibrary.network import com.example.paginglibrary.model.UserResponse import retrofit2.Call import retrofit2.http.GET import retrofit2.http.Query /** * Created by Morris on 03,June,2019 */ interface ApiService { @GET("users") fun getUsers(@Query("page") page: Int): Call<UserResponse> }
package com.example.paginglibrary.network; import com.example.paginglibrary.model.UserResponse; import retrofit2.Call; import retrofit2.http.GET; import retrofit2.http.Query; public interface ApiService { @GET("users") Call<UserResponse> getUsers(@Query("page") long page); }
3.2 Retrofit Service Builder
Your interface is ready to use, let’s create a web service builder class. To do that create a class named is ApiServiceBuilder.
package com.example.paginglibrary.network import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory object ApiServiceBuilder { // Base URL private const val URL = "https://reqres.in/api/" // Create Logger private val logger = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY) // Create OkHttp Client private val okHttp = OkHttpClient.Builder() .addInterceptor(logger) // Create Retrofit Builder private val builder = Retrofit.Builder() .baseUrl(URL) .addConverterFactory(GsonConverterFactory.create()) .client(okHttp.build()) // Create Retrofit Instance private val retrofit = builder.build() fun <T> buildService(serviceType: Class<T>): T { return retrofit.create(serviceType) } }
package com.example.paginglibrary.network; import okhttp3.OkHttpClient; import okhttp3.logging.HttpLoggingInterceptor; import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; public class ApiServiceBuilder { // Base URL private static final String URL = "https://reqres.in/api/"; // Create Logger private static HttpLoggingInterceptor logger = new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY); // Create OkHttp Client private static OkHttpClient.Builder okHttp = new OkHttpClient.Builder().addInterceptor(logger); // Create Retrofit Builder private static Retrofit.Builder builder = new Retrofit.Builder() .baseUrl(URL) .addConverterFactory(GsonConverterFactory.create()) .client(okHttp.build()); // Create Retrofit Instance private static Retrofit retrofit = builder.build(); public static <T> T buildService(Class<T> type) { return retrofit.create(type); } }
4. Create a Data Source
Data Source is an Incremental data loader for page-keyed content, where requests return keys for next/previous pages. Implement a DataSource using PageKeyedDataSource if you need to use data from page {@code N – 1} to load page {@code N}. This is common, for example, in network APIs that include a next/previous link or key with each page load. In paging library the data source class act as a repository.
In data source, we have to override 3 methods.
- loadInitial – It loads the first page of your data that use to initialize RecyclerView
- loadBefore – it used when to scroll up
- loadAfter – is called when natural scroll down
That is essential needed for a paged key DataSource
package com.example.paginglibrary.model import androidx.paging.PageKeyedDataSource import com.example.paginglibrary.network.ApiService import com.example.paginglibrary.network.ApiServiceBuilder import retrofit2.Call import retrofit2.Callback import retrofit2.Response /** * Created by Morris on 03,June,2019 */ class UserDataSource : PageKeyedDataSource<Int, User>() { override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, User>) { val service = ApiServiceBuilder.buildService(ApiService::class.java) val call = service.getUsers(params.key) call.enqueue(object : Callback<UserResponse> { override fun onResponse(call: Call<UserResponse>, response: Response<UserResponse>) { if (response.isSuccessful) { val apiResponse = response.body()!! val responseItems = apiResponse.users val key = if (params.key > 1) params.key - 1 else 0 responseItems?.let { callback.onResult(responseItems, key) } } } override fun onFailure(call: Call<UserResponse>, t: Throwable) { } }) } override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, User>) { val service = ApiServiceBuilder.buildService(ApiService::class.java) val call = service.getUsers(FIRST_PAGE) call.enqueue(object : Callback<UserResponse> { override fun onResponse(call: Call<UserResponse>, response: Response<UserResponse>) { if (response.isSuccessful) { val apiResponse = response.body()!! val responseItems = apiResponse.users responseItems?.let { callback.onResult(responseItems, null, FIRST_PAGE + 1) } } } override fun onFailure(call: Call<UserResponse>, t: Throwable) { } }) } override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, User>) { val service = ApiServiceBuilder.buildService(ApiService::class.java) val call = service.getUsers(params.key) call.enqueue(object : Callback<UserResponse> { override fun onResponse(call: Call<UserResponse>, response: Response<UserResponse>) { if (response.isSuccessful) { val apiResponse = response.body()!! val responseItems = apiResponse.users val key = params.key + 1 responseItems?.let { callback.onResult(responseItems, key) } } } override fun onFailure(call: Call<UserResponse>, t: Throwable) { } }) } companion object { const val PAGE_SIZE = 6 const val FIRST_PAGE = 1 } }
package com.example.paginglibrary.model; import androidx.annotation.NonNull; import androidx.paging.PageKeyedDataSource; import com.example.paginglibrary.network.ApiService; import com.example.paginglibrary.network.ApiServiceBuilder; import java.util.List; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; /** * Created by Morris on 03,June,2019 */ public class UserDataSource extends PageKeyedDataSource<Long, User> { public static int PAGE_SIZE = 6; public static long FIRST_PAGE = 1; @Override public void loadInitial(@NonNull final LoadInitialParams<Long> params, @NonNull final LoadInitialCallback<Long, User> callback) { ApiService service = ApiServiceBuilder.buildService(ApiService.class); Call<UserResponse> call = service.getUsers(FIRST_PAGE); call.enqueue(new Callback<UserResponse>() { @Override public void onResponse(Call<UserResponse> call, Response<UserResponse> response) { UserResponse apiResponse = response.body(); if (apiResponse != null) { List<User> responseItems = apiResponse.getUsers(); callback.onResult(responseItems, null, FIRST_PAGE + 1); } } @Override public void onFailure(Call<UserResponse> call, Throwable t) { } }); } @Override public void loadBefore(@NonNull final LoadParams<Long> params, @NonNull final LoadCallback<Long, User> callback) { ApiService apiService = ApiServiceBuilder.buildService(ApiService.class); Call<UserResponse> call = apiService.getUsers(params.key); call.enqueue(new Callback<UserResponse>() { @Override public void onResponse(Call<UserResponse> call, Response<UserResponse> response) { UserResponse apiResponse = response.body(); if (apiResponse != null) { List<User> responseItems = apiResponse.getUsers(); long key; if (params.key > 1) { key = params.key - 1; } else { key = 0; } callback.onResult(responseItems, key); } } @Override public void onFailure(Call<UserResponse> call, Throwable t) { } }); } @Override public void loadAfter(@NonNull final LoadParams<Long> params, @NonNull final LoadCallback<Long, User> callback) { ApiService service = ApiServiceBuilder.buildService(ApiService.class); Call<UserResponse> call = service.getUsers(params.key); call.enqueue(new Callback<UserResponse>() { @Override public void onResponse(Call<UserResponse> call, Response<UserResponse> response) { UserResponse apiResponse = response.body(); if (apiResponse != null) { List<User> responseItems = apiResponse.getUsers(); callback.onResult(responseItems, params.key + 1); } } @Override public void onFailure(Call<UserResponse> call, Throwable t) { } }); } }
5. Create a DataSource.Factory
Using Data Source we need DataSource.Factory which extends DataSource.Factory. Basically it just a factory for DataSources. So first create an instance of data source so that we can use it in our RecyclerView.
package com.example.paginglibrary.model import androidx.lifecycle.MutableLiveData import androidx.paging.DataSource /** * Created by Morris on 03,June,2019 */ class UserDataSourceFactory : DataSource.Factory<Int, User>() { val userLiveDataSource = MutableLiveData<UserDataSource>() override fun create(): DataSource<Int, User> { val userDataSource = UserDataSource() userLiveDataSource.postValue(userDataSource) return userDataSource } }
package com.example.paginglibrary.model; import androidx.lifecycle.MutableLiveData; import androidx.paging.DataSource; /** * Created by Morris on 03,June,2019 */ public class UserDataSourceFactory extends DataSource.Factory<Long, User> { public MutableLiveData<UserDataSource> userLiveDataSource=new MutableLiveData<>(); @Override public DataSource<Long, User> create() { UserDataSource userDataSource = new UserDataSource(); userLiveDataSource.postValue(userDataSource); return userDataSource; } }
As you see here only one method that we override method name is create(). However, we need to create a second function here called getMutableLiveData(), which give us back LiveData object.
6. Setup ViewModel
Let’s see the below ViewModel class here’ LivePagedListBuilder<Long, User> ‘ LivePagedListBuilder<Long, User> first argument is page number and is data object.
package com.example.paginglibrary.viewmodel import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel import androidx.paging.LivePagedListBuilder import androidx.paging.PagedList import com.example.paginglibrary.model.User import com.example.paginglibrary.model.UserDataSource import com.example.paginglibrary.model.UserDataSourceFactory /** * Created by Morris on 03,June,2019 */ class UserViewModel : ViewModel() { var userPagedList: LiveData<PagedList<User>> private var liveDataSource: LiveData<UserDataSource> init { val itemDataSourceFactory = UserDataSourceFactory() liveDataSource = itemDataSourceFactory.userLiveDataSource val config = PagedList.Config.Builder() .setEnablePlaceholders(false) .setPageSize(UserDataSource.PAGE_SIZE) .build() userPagedList = LivePagedListBuilder(itemDataSourceFactory, config) .build() } }
package com.example.paginglibrary.viewmodel; import androidx.lifecycle.LiveData; import androidx.lifecycle.ViewModel; import androidx.paging.LivePagedListBuilder; import androidx.paging.PagedList; import com.example.paginglibrary.model.User; import com.example.paginglibrary.model.UserDataSource; import com.example.paginglibrary.model.UserDataSourceFactory; public class UserViewModel extends ViewModel { public LiveData<PagedList<User>> userPagedList; private LiveData<UserDataSource> liveDataSource; public UserViewModel() { init(); } private void init() { UserDataSourceFactory itemDataSourceFactory = new UserDataSourceFactory(); liveDataSource = itemDataSourceFactory.userLiveDataSource; PagedList.Config config = new PagedList.Config.Builder() .setEnablePlaceholders(false) .setPageSize(UserDataSource.PAGE_SIZE) .build(); userPagedList = new LivePagedListBuilder<>(itemDataSourceFactory, config).build(); } }
7. Set up PagedListAdapter
Now we have data ready to load in page by page fashion. We need to create an adapter that accepts this data. In meanwhile do that we need to implement PagedListAdapter. You see this internally extends RecyclerView.Adapter. Basically, PagedListAdapter is a RecyclerView adapter the load’s data into the views piece by piece or page by page. Let’s do that
7.1 Create layout for list item
Simply, create a layout for list item which includes ImageView and two TextView like below
<?xml version="1.0" encoding="utf-8"?> <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:bind="http://schemas.android.com/apk/res-auto" xmlns:card_view="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:elevation="3dp" card_view:cardCornerRadius="1dp" tools:ignore="UnusedAttribute" > <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <androidx.appcompat.widget.AppCompatImageView android:id="@+id/user_avatar" android:layout_width="120dp" android:layout_height="120dp" android:layout_marginBottom="8dp" android:layout_marginLeft="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:background="?attr/selectableItemBackgroundBorderless" android:scaleType="fitXY" bind:layout_constraintBottom_toBottomOf="parent" bind:layout_constraintStart_toStartOf="parent" bind:layout_constraintTop_toTopOf="parent" /> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/user_name" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_below="@+id/user_avatar" android:layout_marginEnd="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:padding="4dp" android:textColor="@color/colorPrimary" android:textSize="20sp" bind:layout_constraintEnd_toEndOf="parent" bind:layout_constraintStart_toEndOf="@+id/user_avatar" bind:layout_constraintTop_toTopOf="parent" tools:text="Morris" /> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/user_email" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_below="@+id/user_name" android:layout_marginEnd="8dp" android:layout_marginStart="8dp" android:layout_marginTop="4dp" android:padding="4dp" android:textColor="@color/colorAccent" android:textSize="16sp" bind:layout_constraintEnd_toEndOf="parent" bind:layout_constraintStart_toEndOf="@+id/user_avatar" bind:layout_constraintTop_toBottomOf="@+id/user_name" tools:text="morris@gmail.com" /> </androidx.constraintlayout.widget.ConstraintLayout> </androidx.cardview.widget.CardView>
7.2 Furthermore, create PagedListAdapter
Now I’m creating PagedListAdapter that present data from a PagedList. Basically, It is responsible for presenting data loaded by PagedList and It’s the Implementation of the RecyclerView.Adapter.
package com.example.paginglibrary.ui import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.paging.PagedListAdapter import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide import com.example.paginglibrary.R import com.example.paginglibrary.model.User import kotlinx.android.synthetic.main.user_list_item.view.* class UserAdapter : PagedListAdapter<User, UserAdapter.UserViewHolder>(USER_COMPARATOR) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.user_list_item, parent, false) return UserViewHolder(view) } override fun onBindViewHolder(holder: UserViewHolder, position: Int) { val user = getItem(position) user?.let { holder.bind(it) } } class UserViewHolder(view: View) : RecyclerView.ViewHolder(view) { private val imageView = view.user_avatar private val userName = view.user_name private val userEmail = view.user_email fun bind(user: User) { userName.text = user.firstName + " " + user.lastName userEmail.text = user.email Glide.with(imageView.context) .load(user.avatar) .placeholder(R.drawable.loading) .into(imageView); } } companion object { private val USER_COMPARATOR = object : DiffUtil.ItemCallback<User>() { override fun areItemsTheSame(oldItem: User, newItem: User): Boolean = oldItem.id == newItem.id override fun areContentsTheSame(oldItem: User, newItem: User): Boolean = newItem == oldItem } } }
package com.example.paginglibrary.ui; import android.annotation.SuppressLint; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.paging.PagedListAdapter; import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import com.example.paginglibrary.R; import com.example.paginglibrary.model.User; public class UserAdapter extends PagedListAdapter<User, UserAdapter.UserViewHolder> { public UserAdapter() { super(USER_COMPARATOR); } @NonNull @Override public UserViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.user_list_item, parent, false); return new UserViewHolder(view); } @Override public void onBindViewHolder(@NonNull UserViewHolder holder, int position) { holder.bind(getItem(position)); } public class UserViewHolder extends RecyclerView.ViewHolder { private TextView userName; private TextView userEmail; private ImageView userPic; UserViewHolder(@NonNull View itemView) { super(itemView); userName = itemView.findViewById(R.id.user_name); userEmail = itemView.findViewById(R.id.user_email); userPic = itemView.findViewById(R.id.user_avatar); } void bind(User user) { if (user.getFirstName() != null && user.getLastName() != null) { userName.setText(String.format("%s %s", user.getFirstName(), user.getLastName())); } if (user.getEmail() != null) { userEmail.setText(user.getEmail()); } Glide.with(itemView.getContext()) .load(user.getAvatar()) .placeholder(R.drawable.loading) .into(userPic); } } private static final DiffUtil.ItemCallback<User> USER_COMPARATOR = new DiffUtil.ItemCallback<User>() { @Override public boolean areItemsTheSame(@NonNull User oldItem, @NonNull User newItem) { return oldItem.getId() == newItem.getId(); } @SuppressLint("DiffUtilEquals") @Override public boolean areContentsTheSame(@NonNull User oldItem, @NonNull User newItem) { return oldItem == newItem; } }; }
8. Add Paging Stuff in MainActivity
So all paging related stuff is ready to use, In MainActivity bind views using data binding. After that, we need to set data over RecyclerView. let’s do that
8.1 Add RecyclerView main activity layout file
<?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" android:padding="4dp" tools:showIn="@layout/activity_main" > <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="0dp" android:layout_height="0dp" android:layout_marginBottom="8dp" android:layout_marginEnd="8dp" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" android:layout_marginStart="8dp" android:layout_marginTop="8dp" android:clipToPadding="false" android:scrollbars="vertical" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
8.2 Open the activity and bind the layout item
In Activity file, we simply did bind the XML view to activity file. After that we are observing PagedList and finally updating the adapter
package com.example.paginglibrary.ui import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.LinearLayoutManager import com.example.paginglibrary.R import com.example.paginglibrary.viewmodel.UserViewModel import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val adapter = UserAdapter() recyclerView.layoutManager = LinearLayoutManager(this) val itemViewModel = ViewModelProviders.of(this) .get(UserViewModel::class.java) itemViewModel.userPagedList.observe(this, Observer { adapter.submitList(it) }) recyclerView.adapter = adapter } }
package com.example.paginglibrary.ui; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProviders; import androidx.paging.PagedList; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.example.paginglibrary.R; import com.example.paginglibrary.model.User; import com.example.paginglibrary.viewmodel.UserViewModel; public class MainActivity extends AppCompatActivity { RecyclerView recyclerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); recyclerView = findViewById(R.id.recyclerView); final UserAdapter adapter = new UserAdapter(); recyclerView.setLayoutManager(new LinearLayoutManager(this)); UserViewModel itemViewModel = ViewModelProviders.of(this).get(UserViewModel.class); itemViewModel.userPagedList.observe(this, new Observer<PagedList<User>>() { @Override public void onChanged(PagedList<User> users) { adapter.submitList(users); } }); recyclerView.setAdapter(adapter); } }
Conclusion
In this android app tutorial, We have learned implementation of the paging library. I hope it’s helpful for you, then help me by sharing this post with all your friends who learning android app development.
Get Solution Code
If you have any queries please put your comment below. If you looking to integrate the implementation of the paging library in your android project, Feel free to content us
15 Comments
what if the webservice api has no query for laoding data.i mean no query in ednpoint then how will we load data by paging library
On SwipeRefreshLayout i need to hit api from starting index how it is possible
I am unable to get the the arraylist size
I am using php for backend….see my php code
Please help…..
Hey i want to remove item from list. How can do that. please help me
Thank you very much! Very good tutorial, I’m using it with RxJava2 and it works well!
Thanks a lot.. 🙂 <3
How can I implement Paging Library with Instant Search?
Good job bro… keep it up ..
Thank u very much! Great job!
Thank you so much! This is the best tutorial I have seen for the paged adapter. I couldn’t get my head round many others, they seemed way too complicated. I followed your guide and everything worked perfectly!
Welcome 🙂 Mariano let’s help me with share my blog with your friends
Hi, thanks for this great tutorial.
How can i show load more progressBar ?
Even I too asking the same thing. Can you please update on this?
Sure will update soon