导包
def paging_version = "3.0.0"
dependencies {
implementation "androidx.paging:paging-runtime:$paging_version"
// alternatively - without Android dependencies for tests
testImplementation "androidx.paging:paging-common:$paging_version"
//数据库相关
implementation 'androidx.room:room-runtime:2.2.5'
implementation 'androidx.room:room-common:2.2.5'
implementation 'androidx.room:room-ktx:2.2.5'
kapt 'androidx.room:room-compiler:2.2.5'
}
//使用 "kapt" 必须导入这个
apply plugin: 'kotlin-kapt'
- 本文是从数据库中读取数据显示到RecyclerView,数据库中我已提前insert了数据
数据库相关大代码如下
@Database(entities = [User::class],version = 1,exportSchema = false)
abstract class DemoDataBase : RoomDatabase() {
abstract val userDao:UserDao
companion object{
private const val DB_NAME = "demo_db"
@Volatile
private var instance:DemoDataBase? = null
@Synchronized
fun getInstance(context:Context):DemoDataBase{
return instance?: Room.databaseBuilder(
context,
DemoDataBase::class.java,
DB_NAME)
.allowMainThreadQueries()
.build()
.also { instance = it }
}
}
}
@Entity(tableName = "tb_user")
data class User(
@PrimaryKey
val id:Int,
var name:String?
)
@Dao
interface UserDao {
@Insert
fun insertUser(user: User)
@Query("select * from tb_user")
fun queryAllUser():List<User>
@Query("select * from tb_user where id>=:start and id< :start+:size")
fun queryPartUser(start:Int,size:Int):List<User>
}
创建PagingResource
class MyPagingResource(private val userDao: UserDao) : PagingSource<Int, User>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, User> {
var result: LoadResult<Int, User> = LoadResult.Error(Exception("加载中..."))
try {
var nextPageNum = params.key ?: 1
//从数据库读取数据 每次只获取部分内容 模拟分页获取
val resultData = userDao.queryPartUser(nextPageNum, params.loadSize)
result = LoadResult.Page<Int, User>(
data = resultData ?: emptyList(),
prevKey = if (nextPageNum == 1) null else nextPageNum - params.loadSize,
nextKey = nextPageNum + params.loadSize
)
} catch (e: Exception) {
Log.e("DEMO_TAG", "load: 加载异常")
result = LoadResult.Error(e)
}
return result
}
override fun getRefreshKey(state: PagingState<Int, User>): Int? = null
}
上面的代码实现了按页码加载各页对象的 PagingSource。泛型中Key 类型为 Int,Value 类型为 User。key指代页码,value指代需要加载的数据类型。重写load函数,并在函数中获取数据。函数返回一个LoadResult。在获取到数据之后,调用LoadResult.Page 将数据赋给data参数,再指定好上一页和下一页的页码。另外重写了getRefreshKey方法,这个方法在更高级的用法中才会用到,这里直接返回null好了
设置 PagingData 流
fun getPagingData(): Flow<PagingData<User>> {
val config =
PagingConfig(
pageSize = 10,
prefetchDistance = 5,
initialLoadSize = 5,
maxSize = 10 * 3
)
return Pager(config = config) {
MyPagingResource(userDao)
}.flow
}
定义 RecyclerView 适配器
class MyPagingAdapter : PagingDataAdapter<User, MyPagingAdapter.UserVH>(differCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = UserVH.createVH(parent)
override fun onBindViewHolder(holder: UserVH, position: Int) {
getItem(position)!!.let {
holder.bind(it)
}
}
companion object {
private val differCallback = 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
}
}
}
class UserVH(private val binding: ItemBinding) : RecyclerView.ViewHolder(binding.root) {
companion object {
fun createVH(parent: ViewGroup): UserVH {
return UserVH(
ItemBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
}
fun bind(user: User) {
binding.user = user
binding.executePendingBindings()
}
}
}
Paging 库提供了 PagingDataAdapter 类,定义一个扩展 PagingDataAdapter 的类。
适配器还必须定义 onCreateViewHolder() 和 onBindViewHolder() 方法,并指定 DiffUtil.ItemCallback。这与定义 RecyclerView 列表 Adapter 时的通常做法相同
显示分页数据
userDao = DemoDataBase.getInstance(this).userDao
// repeat(100){
// userDao.insertUser(User(it,"name__$it"))
// }
val rv = findViewById<RecyclerView>(R.id.rv)
val linearLayoutManager = LinearLayoutManager(this)
linearLayoutManager.orientation = RecyclerView.VERTICAL
rv.layoutManager = linearLayoutManager
val myPagingAdapter = MyPagingAdapter()
rv.adapter = myPagingAdapter
lifecycleScope.launchWhenStarted {
getPagingData().cachedIn(this).collectLatest {
myPagingAdapter.submitData(it)
}
}
页脚添加下拉刷新状态
页面布局 footer_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:padding="10dp">
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="center" />
<TextView
android:id="@+id/retry_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:text="Loading..." />
</LinearLayout>
</layout>
适配器 继承自LoadStateAdapter类
class MyFooterLoadStateAdapter :LoadStateAdapter<FooterViewHolder>(){
override fun onBindViewHolder(holder: FooterViewHolder, loadState: LoadState) {
holder.bind(loadState)
}
override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): FooterViewHolder {
return FooterViewHolder(
FooterItemBinding.inflate(LayoutInflater.from(parent.context),parent,false),
loadState
)
}
}
class FooterViewHolder(
private val binding: FooterItemBinding,
private val loadState: LoadState
) : RecyclerView.ViewHolder(binding.root) {
fun bind(loadState: LoadState){
Log.e("DEMO_TAG", "bind: ----加载状态 = $loadState")
binding.executePendingBindings()
}
}
使加载状态生效,需要改动一下
rv.adapter = myPagingAdapter.withLoadStateFooter(MyFooterLoadStateAdapter())
添加header状态同理