Compose中Paging3、SwipeRefresh配合实现下拉刷新和自动加载

简介: Compose中Paging3、SwipeRefresh配合实现下拉刷新和自动加载

概述


Compose中Paging3的使用和Recycleview中Paging3的使用基本一致,不同的是Compose中我们的ui使用LazyColumn来承载数据。

我们需要做的事情如下:

  • 配置PagingSource
  • 配置数据类SimpleUseBean
  • 配置ViewModel
  • 在LazyColumn中渲染数据


简单加载数据


依赖

var paging_version = "3.0.1"
    implementation("androidx.paging:paging-runtime:$paging_version")
    testImplementation("androidx.paging:paging-common:$paging_version")
    implementation("androidx.paging:paging-compose:1.0.0-alpha12")
复制代码


数据类

data class SimpleUseBean(val data: String="")
复制代码


ViewModel

class SimpleUseViewModel : ViewModel() {
    val projects = Pager(PagingConfig(pageSize = 20)){
        SimpleUseSource()
    }.flow.cachedIn(viewModelScope)
}
复制代码


配置PagingSource

class SimpleUseSource : PagingSource<Int, SimpleUseBean>() {
    override fun getRefreshKey(state: PagingState<Int, SimpleUseBean>): Int? =null
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, SimpleUseBean> {
        return try {
            val nextPage = params.key ?: 1
            val datas = mutableListOf(
                SimpleUseBean("哈哈${params.key}"),
                SimpleUseBean("哈哈${params.key}"),
                SimpleUseBean("哈哈${params.key}"),
                SimpleUseBean("哈哈${params.key}"),
                SimpleUseBean("哈哈${params.key}")
            )
            LoadResult.Page(
                data = datas,
                prevKey = if (nextPage == 1) null else nextPage - 1,
                nextKey = if (nextPage < 100) nextPage + 1 else null
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }
}
复制代码


渲染数据

@Composable
fun simpleUse() {
    val model = viewModel<SimpleUseViewModel>()
    val datas = model.projects.collectAsLazyPagingItems()
    LazyColumn(content = {
        itemsIndexed(datas) { _, data ->
            Box(
                Modifier
                    .padding(horizontal = 14.dp,vertical = 4.dp)
                    .fillMaxWidth()
                    .height(60.dp)
                    .border(1.dp, Color.Red, RoundedCornerShape(5.dp))
                    .padding(start = 10.dp)
                ,
                contentAlignment = Alignment.CenterStart
            ) {
                Text(text = data?.data ?: "")
            }
        }
    })
}
复制代码


实现效果

image.png


使用Compose、SwipeRefresh、LazyColumn实现刷新和加载数据


依赖

SwipeRefresh依赖

implementation ("com.google.accompanist:accompanist-swiperefresh:0.18.0")
复制代码


PagingSource

class RefreshLoadUseSource : PagingSource<Int, RefreshData>() {
    override fun getRefreshKey(state: PagingState<Int, RefreshData>): Int? = null
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, RefreshData> {
        return try {
            val nextPage = params.key ?: 1
            if (nextPage < 13) {
                delay(1500)
                val datas = mutableListOf(
                    RefreshData("哈哈${params.key}"),
                    RefreshData("哈哈${params.key}"),
                    RefreshData("哈哈${params.key}"),
                    RefreshData("哈哈${params.key}"),
                    RefreshData("哈哈${params.key}")
                )
                LoadResult.Page(
                    data = datas,
                    prevKey = if (nextPage == 1) null else nextPage - 1,
                    nextKey = if (nextPage < 100) nextPage + 1 else null
                )
            } else {//超过13条就加载错误
                LoadResult.Error(NullPointerException("空指针"))
            }
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }
}
复制代码


ViewModel和数据实体

data class RefreshData(val data: String)
复制代码


class RefreshLoadUseViewModel : ViewModel() {
    val datas = Pager(PagingConfig(pageSize = 20)) {
        RefreshLoadUseSource()
    }.flow.cachedIn(viewModelScope)
}
复制代码


ui代码

@Composable
fun refreshLoadUse() {
    val refreshState = rememberSwipeRefreshState(isRefreshing = false)
    val model = viewModel<RefreshLoadUseViewModel>()
    val collectAsLazyPagingItems = model.datas.collectAsLazyPagingItems()
    SwipeRefresh(state = refreshState, onRefresh = {
        collectAsLazyPagingItems.refresh()
    }) {
        LazyColumn(
            modifier = Modifier
                .fillMaxWidth()
                .fillMaxHeight(),
            content = {
                itemsIndexed(collectAsLazyPagingItems) { _, refreshData ->//每个item的展示
                    Box(
                        modifier = Modifier
                            .padding(horizontal = 14.dp, vertical = 4.dp)
                            .fillMaxWidth()
                            .height(50.dp)
                            .background(Color.Green,shape= RoundedCornerShape(8.dp))
                            .border(
                                width = 1.dp,
                                color = Color.Red,
                                shape = RoundedCornerShape(8.dp)
                            )
                            .padding(start = 10.dp),
                        contentAlignment = Alignment.CenterStart
                    ) {
                        Text(text = refreshData?.data ?: "")
                    }
                }
                when (collectAsLazyPagingItems.loadState.append) {
                    is LoadState.Loading -> {//加载中的尾部item展示
                        item {
                            Box(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .height(50.dp),
                                contentAlignment = Alignment.Center
                            ) {
                                Text(text = "加载中。。。")
                            }
                        }
                    }
                    else -> {//加载完成或者加载错误展示的尾部item
                        item {
                            Box(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .height(50.dp),
                                contentAlignment = Alignment.Center
                            ) {
                                Text(text = "--加载完成或加载错误--")
                            }
                        }
                    }
                }
            })
    }
}
复制代码


实现效果

image.png

git: github.com/ananananzhu…


Room+RemoteMediator实现数据的插入和删除(2022-04-17补充)


太累了,这次补充直接上代码和效果了

代码

@Composable
fun RoomLoadPage() {
    val model: RoomLoadPageModel = viewModel()
    val pagingData = model.projects.collectAsLazyPagingItems()
//    val listState = rememberLazyListState()
    val swipeState = rememberSwipeRefreshState(isRefreshing = false)
    Column {
        Button(onClick = {
            model.insert()
        }) {
            Text(text = "插入数据")
        }
        SwipeRefresh(state = swipeState, onRefresh = {
            pagingData.refresh()
        }) {
            LazyColumn(content = {
                swipeState.isRefreshing = false
                itemsIndexed(pagingData) { index, goods ->
                    if (goods != null) {
                        Box(Modifier
                            .fillMaxWidth()
                            .height(80.dp), contentAlignment = Alignment.Center) {
                            Row(
                                Modifier.fillMaxSize(),
                                verticalAlignment = Alignment.CenterVertically,
                                horizontalArrangement = Arrangement.SpaceAround
                            ) {
                                Text(text = goods.name)
                                Button(onClick = {
                                    model.delete(goods)
                                }) {
                                    Text(text = "删除")
                                }
                                Button(onClick = {
                                    model.modify(goods, index)
                                }) {
                                    Text(text = "修改")
                                }
                            }
                        }
                    }
                }
            })
        }
    }
}
class RoomLoadPageModel : ViewModel() {
    //    val db =
//        Room.databaseBuilder(App.app, GoodsDatabase::class.java, "goods")
//            .allowMainThreadQueries()
//            .build()
    val db =
        Room.inMemoryDatabaseBuilder(App.app, GoodsDatabase::class.java)
            .allowMainThreadQueries()
            .build()
    private var count = 0
    @OptIn(ExperimentalPagingApi::class)
    val projects = Pager(
        PagingConfig(pageSize = 20),
        remoteMediator = RoomRemoteMediator(callback = { loadType, pagingState ->
            Goods("商品:${++count}").let {
                db.goodsDao().insertAll(it)
            }
            RemoteMediator.MediatorResult.Success(false)
        }),
    ) {
        val start = System.currentTimeMillis()
        val data = db.goodsDao().getAll1()
        logEE("加载时间:${System.currentTimeMillis() - start}")
        data
    }.flow.cachedIn(viewModelScope)
    fun insert() {
        viewModelScope.launch {
            db.goodsDao().insertAll(listOf(Goods(name = "aaa${++count}")))
        }
    }
    fun delete(goods: Goods) {
        db.goodsDao().delete(goods)
    }
    override fun onCleared() {
        super.onCleared()
        db.clearAllTables()
    }
    /**
     * 更新item数据
     */
    fun modify(goods: Goods, index: Int) {
        db.goodsDao().update(goods.apply {
            name = "---名字被修改"
        })
    }
}
class RoomLoadPageSource(val callback: suspend (params: LoadParams<Int>) -> LoadResult<Int, Goods>) :
    PagingSource<Int, Goods>() {
    override fun getRefreshKey(state: PagingState<Int, Goods>): Int? = null
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Goods> {
        return callback(params)
    }
}
@OptIn(ExperimentalPagingApi::class)
class RoomRemoteMediator(val callback: suspend (LoadType, PagingState<Int, Goods>) -> MediatorResult) :
    RemoteMediator<Int, Goods>() {
    override suspend fun load(loadType: LoadType, state: PagingState<Int, Goods>): MediatorResult {
        return callback(loadType, state)
    }
}
@Dao
interface GoodsDao {
    @Query("SELECT * FROM goods order by goodsName ASC")
    fun getAll(): List<Goods>
    @Query("SELECT * FROM goods")
    fun getAll1(): PagingSource<Int, Goods>
    @Insert
    fun insertAll(vararg users: Goods)
    @Insert
    fun insertAll(users: List<Goods>)
    @Delete
    fun delete(user: Goods)
    @Update
    fun update(goods: Goods)
}
@Entity
data class Goods(
    @PrimaryKey(autoGenerate = false)
    @ColumnInfo(name = "goodsName")
    var name: String,
) {
//    @PrimaryKey(autoGenerate = true)
//    var id: Long? = null
}
@Database(entities = arrayOf(Goods::class), version = 1)
abstract class GoodsDatabase : RoomDatabase() {
    abstract fun goodsDao(): GoodsDao
}
复制代码


效果

image.png



相关文章
|
5月前
|
安全 JavaScript 前端开发
kotlin开发安卓app,JetPack Compose框架,给webview新增一个按钮,点击刷新网页
在Kotlin中开发Android应用,使用Jetpack Compose框架时,可以通过添加一个按钮到TopAppBar来实现WebView页面的刷新功能。按钮位于右上角,点击后调用`webViewState?.reload()`来刷新网页内容。以下是代码摘要:
|
缓存 Kotlin
开源 | 如何写一个好用的 JetPack Compose 状态页组件
世界很大,也很小,组件很多,也很少。
234 0
egret微信小游戏自定义加载(loading)界面
egret微信小游戏自定义加载(loading)界面
egret微信小游戏自定义加载(loading)界面
|
开发工具 git
Compose中实现原生TabView+ViewPager的效果
Compose中实现原生TabView+ViewPager的效果
1386 0
Compose中实现原生TabView+ViewPager的效果
|
JavaScript
还在多次copy ElTable组件吗?试试dyingtable插件吧
本文主要针对table表格item多次渲染进行了一个二次封装,节省代码量。将table表头和表体数据独立出来,通过js数据去控制其表头展示文字。
170 0
|
Android开发 开发者
【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★(五)
【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★(五)
206 0
【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★(五)
|
开发者
【错误记录】Flutter 设备连接显示 Loading... ( 断网 | 删除 flutter/bin/cache/lockfile 文件 )
【错误记录】Flutter 设备连接显示 Loading... ( 断网 | 删除 flutter/bin/cache/lockfile 文件 )
252 0
【错误记录】Flutter 设备连接显示 Loading... ( 断网 | 删除 flutter/bin/cache/lockfile 文件 )
|
Android开发
【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★(四)
【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★(四)
162 0
|
Android开发
【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★(三)
【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★(三)
124 0
|
存储 Android开发
【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★(一)
【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★(一)
227 0