观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

版权声明

本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/16014823.html

本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。

前言

    Paging是在jetpack系列中的分页框架,在之前Android上做分页框架一般都是需要自己封装RecyclerView来实现。自己封装有一定程度上的复杂性,代码的碎片性与风格不统一性。封装后其他人对代码难以快速理解。这是本人的封装 https://www.cnblogs.com/guanxinjing/p/13204403.html

Paging的出现就是为了在Android平台上标准化分页加载功能。说句实话,其实Paging也相当的复杂与碎片。 但是Paging也有闪光点比如数据预加载、性能消耗低、稳定性高。

google文档 https://developer.android.google.cn/jetpack/androidx/releases/paging

代码

 效果图

 

 

 

DemoBean

data class DemoBean(val name: String)

PagingSource

数据来源类,下面的代码是使用了模拟数据。在实际开发中这里的数据是从数据库或者网络请求中获取的

import android.util.Log
import androidx.paging.PagingSource
import androidx.paging.PagingState

class MyPagingSource : PagingSource<Long, DemoBean>() {

    override suspend fun load(params: LoadParams<Long>): LoadResult<Long, DemoBean> {
        try {
            val currentPage = params.key ?: 0
            //上一页
            val prevKey = if (currentPage == 0L) null else currentPage
            //下一页
            val nextkey = currentPage + 1

            val dataList = createData(currentPage, params.loadSize)

            if (nextkey >= 10L) {
                //模拟当前没有数据了,实际项目里这里可以根据服务器返回或者数据库返回的数据判断
                return LoadResult.Page(
                    dataList,
                    null,
                    null
                )
            }
            return LoadResult.Page(
                dataList,
                prevKey,
                nextkey
            )
        } catch (e: Exception) {
            return LoadResult.Error(e)
        }
    }

    override fun getRefreshKey(state: PagingState<Long, DemoBean>): Long? {
        Log.e("zh", "getRefreshKey: ")
        return 0L
    }

    /*
        创建模拟数据
     */
    var mItemCount = 0
    fun createData(currentPage: Long, pageSize: Int): List<DemoBean> {
        Log.e("zh", "createData: currentPage = ${currentPage} mItemCount = $mItemCount  pageSize = ${pageSize}" )
        val list = mutableListOf<DemoBean>()
        for (item in 0 until 10) {
            list.add(DemoBean("$mItemCount"))
            mItemCount++
        }
        return list
    }
}

ViewModel

import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.asLiveData
import androidx.lifecycle.viewModelScope
import androidx.paging.*

class PagingViewModel : ViewModel() {

    fun getData(): LiveData<PagingData<DemoBean>> {
        return Pager(PagingConfig(10), initialKey = 0) { MyPagingSource() }
            .flow
            .cachedIn(viewModelScope)
            .asLiveData(viewModelScope.coroutineContext)
    }
}

Adapter

import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.example.myapplication.databinding.ItemPagingBinding
import kotlinx.coroutines.CoroutineDispatcher

class PagingAdapter : PagingDataAdapter<DemoBean, PagingAdapter.ViewHolder> {

    constructor() : super(object : DiffUtil.ItemCallback<DemoBean>() {
        override fun areItemsTheSame(oldItem: DemoBean, newItem: DemoBean): Boolean {
            Log.e("zh", "areItemsTheSame: ")
            return false
        }

        override fun areContentsTheSame(oldItem: DemoBean, newItem: DemoBean): Boolean {
            Log.e("zh", "areContentsTheSame: ")
            return false
        }
    })

    constructor(diffCallback: DiffUtil.ItemCallback<DemoBean>) : super(diffCallback)

    constructor(diffCallback: DiffUtil.ItemCallback<DemoBean>, mainDispatcher: CoroutineDispatcher, workerDispatcher: CoroutineDispatcher) : super(diffCallback, mainDispatcher, workerDispatcher)


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val binding =
            ItemPagingBinding.inflate(
                LayoutInflater.from(parent.context), parent, false
            )
        return ViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.mBinding.text1.text = getItem(position)?.name
    }

    class ViewHolder(var mBinding: ItemPagingBinding) : RecyclerView.ViewHolder(mBinding.root)
}

xml

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".paging.PagingActivity">

    <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
        android:id="@+id/refresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

Activity

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.myapplication.databinding.ActivityPagingBinding

class PagingActivity : AppCompatActivity() {
    private val mBinding by lazy { ActivityPagingBinding.inflate(layoutInflater) }
    private val mViewModel by lazy { ViewModelProvider(this).get(PagingViewModel::class.java) }
    private val mAdapter by lazy { PagingAdapter() }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(mBinding.root)
        mBinding.recyclerView.layoutManager = LinearLayoutManager(this)
        mBinding.recyclerView.adapter = mAdapter

        //刷新
        mBinding.refresh.setOnRefreshListener {
            //这个refresh 方法是PagingDataAdapter的方法
            mAdapter.refresh()
        }
        //观察数据返回
        mViewModel.getData().observe(this, Observer() {
            if (mBinding.refresh.isRefreshing) mBinding.refresh.isRefreshing = false
            lifecycleScope.launchWhenCreated {
                mAdapter.submitData(it)
            }
        })
    }
}

 

 

添加页头页脚

withLoadStateHeader 添加页脚,可以用于loadmore

withLoadStateHeaderAndFooter 可以添加页头/页脚

withLoadStateFooter 添加页头

这里需要注意的是,具体页头页脚的实现方式也是创建一个ViewHolder然后放到LoadStateAdapter中去,我们常见的底部loadmore就是添加页脚,但是这里的Header不是我们项目中在列表最顶部添加一个item的意思,而是和loadmore类似的概念。也就是说如果我们添加了一个页头,那么只有在PagingSource中返回LoadResult.Page的时候prevKey不为null才会显示出来,所以如果我们从第一页开始加载是看不到这个Header的,如果我们一开始加载的页数是第5页,那么我们在往上滑动的时候,才能看到我们的Header

监听状态

想要监听数据获取的状态在PagingDataAdapter里有两个方法

addDataRefreshListener 这个方法是当新的PagingData被提交并且显示的回调

addLoadStateListener这个相较于上面那个比较复杂,listener中的回调会返回一个CombinedLoadStates对象

 

 

整理中待续。。。

 

End

posted on 2022-03-16 22:19  观心静  阅读(720)  评论(0编辑  收藏  举报