1. Introduction
In order to solve the loading more functions of RecyclerView, Google launched the Paging component of Jetpack. The Paging component enables more data to be loaded smoothly and seamlessly.
Paging uses a DataSource to update data. They are divided into three types according to their use.
PagaKeyedDataSource<Key, Value>
: Applies to scenarios where the target data is sorted by page number.key
Is the number of pages, the data parameter of the request is reincludednext/previous
Page number information.ItemKeyedDataSource<Key, Value>
: Applies to the loading of target data depending on the information of a particular Item,key
Contains information from Item. For example, the NTH +1 information needs to be loaded based on the NTH Item information. Often used to comment on requests for information.PositionalDataSource<T>
: applies to a fixed target total, loading data through a specific location, key is location information.
Add a dependency to a paging library:
implementation "Androidx. The paging: the paging - runtime: 2.1.1"
implementation "Androidx. The paging: the paging - runtime - KTX: 2.1.1"
implementation 'androidx. Lifecycle: lifecycle - livedata - KTX: 2.2.0'
Copy the code
2. Paging + Room
First of all, this part of the tutorial assumes that you can use Room and ViewModel proficiently, so there will be no detailed explanation of this part of the tutorial.
2.1 Creating a Database
2.1.1 the Entity
@Entity(tableName = "users_table")
data class User(
@PrimaryKey(autoGenerate = true)
val id: Int? = null.@ColumnInfo(name = "first_name")
val firstName: String,
@ColumnInfo(name = "last_name")
val lastName: String,
@ColumnInfo(name = "birthday")
val birthday: String,
@ColumnInfo(name = "nationality")
val nationality: String
)
Copy the code
2.1.2 Dao
One thing to note here is that the return value is not List
but DataSource. Where key is on the left and value is on the right. Key is the internal PositionalDataSource to indicate the location of the current data, i.e. the number of pages. Value obviously shows the data we need to use.
@Dao
interface UserDao {
@Query("SELECT * FROM users_table ORDER BY id ASC")
fun getAllByLivePage(a): DataSource.Factory<Int, User>
}
Copy the code
2.1.3 the Database
@Database(version = 1, entities = [User::class])
abstract class UserDataBase : RoomDatabase() {
companion object {
private var INSTANCE: UserDataBase? = null
fun getInstance(context: Context): UserDataBase? {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(
context,
UserDataBase::class.java.DATABASE_NAME
).build()
}
return INSTANCE
}
fun destroyInstance(a) {
INSTANCE = null
}
const val DATABASE_NAME = "user_database.db"
}
abstract fun getUserDao(a): UserDao
}
Copy the code
2.2 make PagedListAdapter
Instead of inheriting RecyclerAdapter or ListAdapter, we need to inherit PagedListAdapter. The rewritten content is exactly the same as the ListAdapter, nothing special, as follows.
class UserAdapter : PagedListAdapter<User, UserAdapter.UserViewHolder>(diffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding: ItemUserBinding =
DataBindingUtil.inflate(inflater, R.layout.item_user, parent, false)
return UserViewHolder(binding)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int){ getItem(position)? .also { holder.binding.txtBirthday.text = it.birthday holder.binding.txtFirstName.text = it.firstName holder.binding.txtLastName.text = it.lastName holder.binding.txtNationality.text = it.nationality } }class UserViewHolder(var binding: ItemUserBinding) : RecyclerView.ViewHolder(binding.root)
companion object {
private val diffCallback = object : DiffUtil.ItemCallback<User>() {
override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem.id == newItem.id
}
override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem === newItem
}
}
}
}
Copy the code
2.3 Making the Config for Paging
We need to set the Config for Paging in the ViewModel. I have tried setting it in the Activity, but it did not work properly. I wonder if there is anything wrong with my writing method.
class ShowViewModel(application: Application) : AndroidViewModel(application) {
valdao = UserDataBase.getInstance( application.applicationContext )? .getUserDao()valallUsers = dao!! .getAllByLivePage() .toLiveData(Config(pageSize =10, enablePlaceholders = true, maxSize = 50))}Copy the code
The data from DB needs to be converted into the familiar LiveData, and the Config needs to be set at the same time.
pageSize
: The amount of data loaded from the database at one time.enablePlaceholders
: Indicates whether to display if the load fails. The default istrue
.maxSize
: Indicates the maximum amount of data stored in memory. The maximum value of Int by default.prefetchDistance
: Obtain data in advance when the distance to the last data is how far. The default ispageSize
.initialLoadSizeHint
: loaded into thepagedList
Initial amount of data in. The default ispageSize
Three times as much. It’s usually about a normal page of data.
2.4 Monitor and incoming Adapter
The final step is to monitor the Activity and pass the data into the Adapter for updating as it changes.
viewModel.allUsers.observe(this, Observer {
adapter.submitList(it)
})
Copy the code
2.5 making
RoomAndPageListDemo
3. Paging + Network
After Paging + Room, we continue to try the combination of Paging + Network. First of all, I didn’t find a good Api, so I’m doing a local simulation here.
And the PagedListAdapter is exactly the same as above, so I won’t repeat it.
3.1 make a DataSource
We need to inherit PageKeyedDataSource
and override the three methods.
Let’s first look at the data passed in the override function.
params: LoadInitialParams<Int>
: This parameter is the initial loading parameter. Contains two variables,requestedLoadSize
: The amount of data required.placeholdersEnabled
It’s the same thing as above.callback: LoadInitialCallback<Key, Value>
: is a callback after the initial loading is complete.params: LoadParams<Int>
: is the argument passed in when paging forward and backward. Contains two variables,requestedLoadSize
It’s the same as up here,key
Is the current page number.callback: LoadCallback<Key, Value>)
: is the callback after the forward and backward page is finished.
Here are the parameters to override.
loadInitial
: indicates the initial loading.callback
The function is to remindPaging
Data has been loaded.onResult
The first argument to is the loaded data. The second argument is the key of the previous page, which can be set to if the data loaded does not have previous datanull
. The third parameter is the key on the next page, and I’m starting with 0 here, so I’m passing a 1 here.loadBefore
: is the load when the page is turned forward.onResult
The first parameter is also the requested data, and the second parameter is the number of pages loaded forwardparams.key-1
.loadAfter
: is the load when paging backwards.onResult
The first parameter is also the data, and the second parameter is the number of pages when the page is loaded, which is set hereparams.key+1
class UsersDataSource : PageKeyedDataSource<Int, User>() {
// Start loading
override fun loadInitial(
params: LoadInitialParams<Int>,
callback: LoadInitialCallback<Int, User>
) {
callback.onResult(getList(0, params.requestedLoadSize), null.1)}// forward load
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, User>) {
callback.onResult(getList(params.key, params.requestedLoadSize), params.key - 1)}// load backwards
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, User>) {
callback.onResult(getList(params.key, params.requestedLoadSize), params.key + 1)}}Copy the code
3.2 make DataSourceFactory
We need to use DataSource.Factory to generate the UsersDataSource we just made. We need to inherit the datasourec. Factory
that appears in Paging+Room above.
class CustomPageDataSourceFactory() : DataSource.Factory<Int, User>() {
override fun create(a): DataSource<Int, User> {
return UsersDataSource()
}
}
Copy the code
3.3 Generate LiveData through LivePagedListBuilder
Finally, we will set Paging in the Activity. Two parameters need to be passed in the LivePagedListBuilder. Factory
The second one is pagedlist. Config, which is skipped because it’s described above.
// Generate LiveData from LivePagedListBuilder
val data = LivePagedListBuilder(
CustomPageDataSourceFactory(),
PagedList.Config.Builder()
.setPageSize(20)
.setInitialLoadSizeHint(60)
.build()
).build()
// Monitor data and pass it to adapter when changes occur
data.observe(this, Observer {
adapter.submitList(it)
})
Copy the code
3.4 making
PagingDemo
4. Conclusion
The Paging library enables seamless data loading and better user experience. In particular, there is better support for Room, which can reduce the amount of development.