Android开发 Paging3
前言
Paging是在jetpack系列中的分页框架,在之前Android上做分页框架一般都是需要自己封装RecyclerView来实现。自己封装有一定程度上的复杂性,代码的碎片性与风格不统一性。封装后其他人对代码难以快速理解。这是本人的封装 https://www.cnblogs.com/guanxinjing/p/13204403.html
Paging的出现就是为了在Android平台上标准化分页加载功能。说句实话,其实Paging也相当的复杂与碎片。 但是Paging也有闪光点比如数据预加载、性能消耗低、稳定性高。
代码
效果图
DemoBean
data class DemoBean(val name: String)
PagingSource
数据来源类,下面的代码是使用了模拟数据。在实际开发中这里的数据是从数据库或者网络请求中获取的
import android.util.Log import androidx.paging.PagingSource import androidx.paging.PagingState class MyPagingSource : PagingSource() { override suspend fun load(params: LoadParams ): LoadResult { 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? { Log.e("zh", "getRefreshKey: ") return 0L } /* 创建模拟数据 */ var mItemCount = 0 fun createData(currentPage: Long, pageSize: Int): List { Log.e("zh", "createData: currentPage = ${currentPage} mItemCount = $mItemCount pageSize = ${pageSize}" ) val list = mutableListOf () 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> { 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{ constructor() : super(object : DiffUtil.ItemCallback () { 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 ) : super(diffCallback) constructor(diffCallback: DiffUtil.ItemCallback , 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