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



相关文章
Jetpack Compose中ViewModel、Flow、Hilt、Coil的使用
Jetpack Compose中ViewModel、Flow、Hilt、Coil的使用
1801 0
Jetpack Compose中ViewModel、Flow、Hilt、Coil的使用
|
XML 前端开发 IDE
在 Compose 中使用 Jetpack 组件库
在 Compose 中使用 Jetpack 组件库
998 0
|
存储 缓存 Android开发
安卓Jetpack Compose+Kotlin, 使用ExoPlayer播放多个【远程url】音频,搭配Okhttp库进行下载和缓存,播放完随机播放下一首
这是一个Kotlin项目,使用Jetpack Compose和ExoPlayer框架开发Android应用,功能是播放远程URL音频列表。应用会检查本地缓存,如果文件存在且大小与远程文件一致则使用缓存,否则下载文件并播放。播放完成后或遇到异常,会随机播放下一首音频,并在播放前随机设置播放速度(0.9到1.2倍速)。代码包括ViewModel,负责音频管理和播放逻辑,以及UI层,包含播放和停止按钮。
|
Android开发
android Compose中沉浸式设计、导航栏、状态栏的处理
android Compose中沉浸式设计、导航栏、状态栏的处理
2762 0
android Compose中沉浸式设计、导航栏、状态栏的处理
|
XML 存储 JSON
Android Jetpack组件 DataStore的使用和简单封装
Android Jetpack组件 DataStore的使用和简单封装
1355 0
Android Jetpack组件 DataStore的使用和简单封装
|
5月前
|
安全 Java Android开发
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
261 0
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
|
XML 存储 Android开发
Android实战经验之Kotlin中快速实现MVI架构
本文介绍MVI(Model-View-Intent)架构模式,强调单向数据流与不可变状态管理,提升Android应用的可维护性和可测试性。MVI分为Model(存储数据)、View(展示UI)、Intent(用户动作)、State(UI状态)与ViewModel(处理逻辑)。通过Kotlin示例展示了MVI的实现过程,包括定义Model、State、Intent及创建ViewModel,并在View中观察状态更新UI。
562 12
|
编译器 Android开发 开发者
带你了解Android Jetpack库中的依赖注入框架:Hilt
本文介绍了Hilt,这是Google为Android开发的依赖注入框架,基于Dagger构建,旨在简化依赖注入过程。Hilt通过自动化的组件和注解减少了DI的样板代码,提高了应用的可测试性和可维护性。文章详细讲解了Hilt的主要概念、基本用法及原理,帮助开发者更好地理解和应用Hilt。
530 8
|
Android开发 Kotlin
kotlin安卓开发【Jetpack Compose】:封装SnackBarUtil工具类方便使用
GPT-4o 是一个非常智能的模型,比当前的通义千问最新版本在能力上有显著提升。作者让GPT开发一段代码,功能为在 Kotlin 中使用 Jetpack Compose 框架封装一个 Snackbar 工具类,方便调用
|
JavaScript Java Android开发
kotlin安卓在Jetpack Compose 框架下跨组件通讯EventBus
**EventBus** 是一个Android事件总线库,简化组件间通信。要使用它,首先在Gradle中添加依赖`implementation &#39;org.greenrobot:eventbus:3.3.1&#39;`。然后,可选地定义事件类如`MessageEvent`。在活动或Fragment的`onCreate`中注册订阅者,在`onDestroy`中反注册。通过`@Subscribe`注解方法处理事件,如`onMessageEvent`。发送事件使用`EventBus.getDefault().post()`。