Paging Library Trong Android
This post hasn't been updated for 6 years
Giới thiệu
Paging
được google mới cho ra mắt trong bộ Android JetPack của mình- Thành phần chính của
Paging
làDataSource
,PagedList
vàPagedListAdapter
các bạn có thể tìm hiểu thêm ở đây - Bài này chủ yếu mình muốn hướng đẫn cách sử dụng đơn giản của
Paging
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ênPaging
đó 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
}
}
3.Tạo UsersDataSourceFactory
- Dùng để khởi tạo
DataSource
cung cấp dữ liệu choPagedList
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
}
}
4.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
}
}
}
}
5.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()
}
}
6.Tạo RecyclerView
userAdapter = UserAdapter()
usersRecyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL,
false)
usersRecyclerView.adapter = userAdapter
7.Kết quả
- Toàn bộ source code của mình ở đây
- Nguồn : Android Developer và Medium
All Rights Reserved