Some Notes

[Jetpack] Paging Exploring

目前体验下来感觉只是帮我们做了当前页面page的自增,更多的使用方法待补充。

Model

https://jsonplaceholder.typicode.com/,是一个提供假json数据接口的网站,给前端开发者来做Demo最合适不过,这里我拿其中的相册列表接口来做一个相册列表页面。

data class AlbumModel( val albumId: Int, val id: Int, val title: String, val url: String, val thumbnailUrl: String ) interface AlbumDataApi { @GET(“albums/{page}/photos”) suspend fun getAlbumByPage(@Path(“page”) page: Int, @Query(“pageSize”) pageSize: Int): List }

ViewModel

在ViewModel中需要我们去构造一个Pager对象,设置一些分页参数,初始化Item数量,每页的Item数量,预加载大小,最大item数量等。

然后通过Pager中方法可以拿到Flow或者LiveData,我这里选择了使用Flow

class AlbumViewModel : ViewModel() {
 
    private val client = APIClient
    private val pager = Pager(
        config = PagingConfig(
            initialLoadSize = AlbumPagingSource.PAGE_SIZE,
            pageSize = AlbumPagingSource.PAGE_SIZE,
            prefetchDistance = AlbumPagingSource.PAGE_SIZE,
            enablePlaceholders = false
        ),
        pagingSourceFactory = { AlbumPagingSource(client) }
    )
 
    //返回Album数据流
    fun getHomeDataFlow(): Flow<PagingData<AlbumModel>> {
        return pager.flow
    }
 
}

RecycleView 适配器

继承PagingDataAdatper,构造参数需要一个DiffUtil,方法中用相册的Id来判断是不是同一个相册Item

同样需要重写onBindViewHolder、onCreateViewHolder方法和新建类AlbumViewHolder类继承ViewHolder

不过父类中提供了getItem(position: Int)方法来直接获取AlbumModel,解决了之前需要我们自己从数组里取的繁琐。

class AlbumAdapter :
    PagingDataAdapter<AlbumModel, AlbumAdapter.AlbumViewHolder>(DiffCallback) {
 
    companion object {
        private val DiffCallback = object : DiffUtil.ItemCallback<AlbumModel>() {
            override fun areItemsTheSame(oldItem: AlbumModel, newItem: AlbumModel): Boolean {
                return oldItem.id == newItem.id
            }
 
            override fun areContentsTheSame(oldItem: AlbumModel, newItem: AlbumModel): Boolean {
                return oldItem.id == newItem.id
            }
        }
    }
 
    override fun onBindViewHolder(holder: AlbumViewHolder, position: Int) {
        val albumModel = getItem(position)
        albumModel?.let {
            holder.tvAlbumTitle.text = albumModel.title
            Glide.with(holder.itemView.context)
                .load(albumModel.url)
                .into(holder.ivAlbumImage)
        }
    }
 
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AlbumViewHolder {
        ...
    }
 
    class AlbumViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        ...
    }
}
class AlbumPagingSource(private val client: APIClient) : PagingSource<Int, AlbumModel>() {
 
    companion object {
        const val PAGE_SIZE = 10
    }
 
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, AlbumModel> {
        return try {
            val currentPage = params.key ?: 1
            val pageSize = params.loadSize
            val AlbumModels = client.getHomeData(currentPage, pageSize)
            val preKey = if (currentPage > 1) currentPage - 1 else null
            val nextKey = if (AlbumModels.isNotEmpty()) currentPage + 1 else null
            LoadResult.Page(AlbumModels, preKey, nextKey)
        } catch (e: Exception) {
            LoadResult.Error(throwable = e)
        }
 
    }
 
    override fun getRefreshKey(state: PagingState<Int, AlbumModel>): Int? {
        return state.anchorPosition?.let { anchorPosition ->
            state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
                ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
        }
    }

}

Fragment

最后我们在Fragment中收集ViewModel中的AlbumData Flow,通过adapter的submitData方法通知PagingDataAdpater来刷新数据。

    override fun onCreateView(...): View {
        ...
        //给RecycleView设置Adapter和LayoutManager
        binding.rvAlbum.layoutManager = GridLayoutManager(activity, 2)
        binding.rvAlbum.adapter = adapter
 
        //收集相册数据
        lifecycleScope.launch {
            homeViewModel.getHomeDataFlow().collect {
                adapter.submitData(it)
            }
        }
        return binding.root
    }