[Android] Tìm hiểu Paging Library
Tìm hiểu thư viện phân trang Paging Library trong bộ Android JetPack của Android
Giới thiệu
Paging được Google mới cho ra mắt trong bộ Android JetPack với thành phần chính gồm: DataSource, PagedList và PagedListAdapter. Trong bài viết này chủ yếu mình muốn hướng dẫn bạn cách sử dụng Paging thông qua một ví dụ đơn giản.
Sử dụng
1. Thêm Paging vào dependencies ở build.gradle trong module:
dependencies {
...
implementation "android.arch.paging:runtime:1.0.1"
implementation "android.arch.paging:rxjava2:1.0.1"
}
2. Tạo file Service để thực hiện request
interface GithubService {
@GET("/users")
fun getUsers(@Query("since") userId: Long, @Query("per_page") perPage: Int): Single<List<User>>
companion object {
fun getService(): GithubService {
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build()
return retrofit.create(GithubService::class.java)
}
}
}
3. Tạo file UsersDataSource
File này implement ItemKeyedDataSource để override lại 3 phương thức chính để tạo nên Paging đó là :
- loadInitial: Như tên của nó thì nó sẽ gọi khi DataSource được khởi tạo
- loadBefore: Sẽ được gọi sau loadInitial
- loadAfter: Sẽ được gọi khi list của bạn đạt tới limit được khai báo
Code của mình thì sẽ như này:
class UsersDataSource(private val githubService: GithubService,
private val compositeDisposable: CompositeDisposable) : ItemKeyedDataSource<Long, User>() {
val isLoading = MutableLiveData<Boolean>()
val isRefresh = MutableLiveData<Boolean>()
override fun loadInitial(params: LoadInitialParams<Long>, callback: LoadInitialCallback<User>) {
isLoading.postValue(true)
isRefresh.postValue(true)
logi(params.requestedLoadSize.toString())
compositeDisposable.add(githubService.getUsers(1, params.requestedLoadSize)
.subscribe({
isLoading.postValue(false)
isRefresh.postValue(false)
callback.onResult(it)
}, {
isLoading.postValue(false)
isRefresh.postValue(false)
}))
}
override fun loadAfter(params: LoadParams<Long>, callback: LoadCallback<User>) {
isLoading.postValue(true)
compositeDisposable.add(
githubService.getUsers(params.key, params.requestedLoadSize)
.subscribe({ users ->
isLoading.postValue(false)
callback.onResult(users)
}, {
isLoading.postValue(false)
}))
}
override fun loadBefore(params: LoadParams<Long>, callback: LoadCallback<User>) {
}
override fun getKey(item: User): Long {
return item.id
}
}
4. Tạo UsersDataSourceFactory
Dùng để khởi tạo DataSource cung cấp dữ liệu cho PagedList
class UsersDataSourceFactory(private val githubService: GithubService,
private val compositeDisposable: CompositeDisposable) : DataSource.Factory<Long, User>() {
val userDataSourceLiveData = MutableLiveData<UsersDataSource>()
override fun create(): DataSource<Long, User> {
val userDataSource = UsersDataSource(githubService, compositeDisposable)
userDataSourceLiveData.postValue(userDataSource)
return userDataSource
}
}
5. Tạo UserAdapter
Như mọi List thì phải có adapter để show data lên UI
class UserAdapter(
private val retryCallback: () -> Unit) : PagedListAdapter<User, RecyclerView.ViewHolder>(
UserDiffCallback) {
private var isLoading = false
override fun onCreateViewHolder(p0: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
R.layout.item_paging_user -> UserItemViewHolder.create(p0)
R.layout.item_paging_loading -> NetworkStateItemViewHolder.create(p0)
else -> throw IllegalAccessException("unknown view type")
}
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (getItemViewType(position)) {
R.layout.item_paging_user -> (holder as? UserItemViewHolder)?.bindTo(getItem(position))
R.layout.item_paging_loading -> (holder as? NetworkStateItemViewHolder)?.bindTo(
isLoading)
}
}
override fun getItemViewType(position: Int): Int {
return if (isLoading && position == itemCount - 1) {
R.layout.item_paging_loading
} else {
R.layout.item_paging_user
}
}
override fun getItemCount(): Int {
return super.getItemCount() + if (isLoading) 1 else 0
}
fun setLoading(isLoading: Boolean?) {
isLoading?.let {
currentList?.isNotEmpty()?.let {
val previousState = this.isLoading
this.isLoading = isLoading
if (previousState != isLoading) {
if (!isLoading) {
notifyItemRemoved(super.getItemCount())
} else {
notifyItemInserted(super.getItemCount())
}
}
}
notifyItemInserted(super.getItemCount())
}
}
companion object {
val UserDiffCallback = object : DiffUtil.ItemCallback<User>() {
override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem == newItem
}
}
}
}
6. Tạo ViewModel
ViewModel sẽ chịu tạo PagedList và cung cấp cho hoạt động để nó có thể thay đổi dữ liệu mỗi khi request từ server.
class PagingViewModel : ViewModel() {
var userList: LiveData<PagedList<User>>
private val compositeDisposable = CompositeDisposable()
private val pageSize = 5
private val sourceFactory: UsersDataSourceFactory
init {
sourceFactory = UsersDataSourceFactory(GithubService.getService(), compositeDisposable)
val config = PagedList.Config.Builder()
.setPageSize(pageSize)
.setInitialLoadSizeHint(pageSize * 2)
.setEnablePlaceholders(false)
.build()
userList = LivePagedListBuilder<Long, User>(sourceFactory, config).build()
}
fun getLoading() = Transformations.switchMap<UsersDataSource, Boolean>(
sourceFactory.userDataSourceLiveData) { it.isLoading }
fun getRefreshState() = Transformations.switchMap<UsersDataSource, Boolean>(
sourceFactory.userDataSourceLiveData) { it.isRefresh }
fun reset() {
sourceFactory.userDataSourceLiveData.value?.invalidate()
}
override fun onCleared() {
super.onCleared()
compositeDisposable.dispose()
}
}
7. Tạo RecyclerView
userAdapter = UserAdapter()
usersRecyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL,
false)
usersRecyclerView.adapter = userAdapter
Kết quả
Toàn bộ source code của mình ở đây
Cảm ơn các bạn đã đọc bài viết.
Chào thân ái và quyết thắng!
Bài viết gốc: https://viblo.asia/p/paging-library-trong-android-bJzKmggrl9N
Theo dõi VnCoder trên Facebook, để cập nhật những bài viết, tin tức và khoá học mới nhất!