Kotlin Jetpack Paging3 和Flow结合使用注意点

2024-06-13 21:06:14 浏览数 (3)

在用DataBinding时要注意DataBinding访问的是静态方法

在Kotlin中就要用companion object和@JvmStatic

代码语言:javascript复制
class ImageViewBindingAdapter {
    //里面的BindingAdapter方法必须是静态方法,否则会编译会报错
    //DataBinding调用必须是静态方法
    companion object {
        @JvmStatic
        @BindingAdapter("image")
        fun setImage(imageView:ImageView, url: String){
            if (!TextUtils.isEmpty(url)){
                //加载网络图片
            } else{
                imageView.setBackgroundColor(Color.GRAY)
            }
        }
    }
}

Paging3分页数据错乱的问题

在计算paging的prevKey和nextKey,也就是上一页,下一页的时候,需要考虑PagingConfig中的initialLoadSize参数

代码语言:javascript复制
fun loadMovie(): Flow<PagingData<Movie>> {
    return Pager(
        config = PagingConfig(
            pageSize = 8,
            //第一次加载的数量,16也就是2页,默认是3*pageSize
            initialLoadSize = 16
        ),
        pagingSourceFactory = { MoviePagingSource() }
    ).flow
}



class MoviePagingSource: PagingSource<Int, Movie>() {
    override fun getRefreshKey(state: PagingState<Int, Movie>): Int? {
        //第一次加载
        return 1
    }

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Movie> {
        val currentPage = params.key ?: 1
        val pageSize = params.loadSize
        val movies = RetrofitClient.createApi(MoviesApi::class.java).getMovies(currentPage, pageSize)

        var prevKey: Int? = null
        var nextKey: Int? = null

        //PagingConfig中的2个参数
        val realPageSize = 8
        val initialLoadSize = 16
        if (currentPage == 1){
            prevKey = null
            nextKey = initialLoadSize / realPageSize   1
        }else{
            prevKey = currentPage - 1
            nextKey = if (movies.hasMore) currentPage   1 else null
        }

        //下面这样计算,在initialLoadSize不等于realPageSize的时候会有数据错乱
        //prevKey = if (currentPage == 1) null else currentPage - 1
        //nextKey = if (movies.hasMore) currentPage   1 else null

        return try {
            LoadResult.Page(
                data = movies.movieList,
                prevKey = prevKey,
                nextKey = nextKey
            )
        }catch (e: Exception){
            e.printStackTrace()
            return LoadResult.Error(e)
        }
    }

}

给Paging加上拉加载更多

代码语言:javascript复制
recycleView.adapter = movieAdapter.withLoadStateFooter(MovieLoadMoreAdapter(this@MainActivity))

只需要加个适配器就可以

代码语言:javascript复制
class MovieLoadMoreAdapter(private val context: Context): LoadStateAdapter<BindViewHolder>() {
    override fun onBindViewHolder(holder: BindViewHolder, loadState: LoadState) {
    }

    override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): BindViewHolder {
        val binding = MovieLoadmoreBinding.inflate(LayoutInflater.from(context), null, false)
        return BindViewHolder(binding)
    }
}

Paging加上下拉刷新

  • 布局加上SwipeRefreshLayout
代码语言:javascript复制
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
    android:id="@ id/swipeRefreshLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

     <androidx.recyclerview.widget.RecyclerView
         android:id="@ id/recycleView"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

 </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
  • 加上刷新监听
代码语言:javascript复制
swipeRefreshLayout.setOnRefreshListener {
	movieAdapter.refresh()
}

  • 监听刷新结束
代码语言:javascript复制
lifecycleScope.launchWhenCreated {
    movieAdapter.loadStateFlow.collectLatest { state ->
    	mBinding.swipeRefreshLayout.isRefreshing = state.refresh is LoadState.Loading
    }
}

下拉刷新后,底部上拉加载更多的loadmore的动画不显示

  • PageConfig还有一个属性是prefetchDistance,预刷新的距离,距离最后一个item多远时加载数据,默认为pageSize
  • 当prefetchDistance很小,并且initialLoadSize也很小时,就会出现上面的bug。比如initialLoadSize=8,prefetchDistance=1时
  • 解决办法也比较简单,2个属性设置的大一点就行了

APP横竖屏切换之后paging加载的数据没有缓存起来

  • ViewModel缓存数据要在属性中
  • 还有就是Paging返回的是flow,需要用cachedIn(viewModelScope)来让Paging的flow的生命周期和ViewModelScope的生命周期保持一致,也就是和activity保持一致
代码语言:javascript复制
class MovieViewModel: ViewModel() {

    private val movies by lazy {
        Pager(
            config = PagingConfig(
                pageSize = 8,
                //第一次加载的数量,16也就是2页
                initialLoadSize = 16,
                //预刷新的距离,距离最后一个item多远时加载数据,默认为pageSize
                prefetchDistance = 8
            ),
            pagingSourceFactory = { MoviePagingSource() }
        ).flow.cachedIn(viewModelScope)
    }

    fun loadMovie(): Flow<PagingData<Movie>> = movies

}

码字不易,求转发,求点在看,求关注,感谢!

0 人点赞