Android Compose Paging3用法

server/2025/3/14 21:55:27/

一、引入包

 implementation(libs.paging.runtime)implementation(libs.paging.compose)
paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "paging_version" }
paging-compose={module="androidx.paging:paging-compose", version.ref = "paging_version" }

版本:

paging_version = "3.3.6"

开发框架:

网络请求【Retrofit】

依赖注入【Koin】

主要使用场景:

1、和PullToRefreshBox一起使用,下拉刷新

2、上拉加载更多

3、对列表操作刷新当前页面

二、使用

1、接口 

@POST("/tangPoetry/queryFavorite")
suspend fun queryFavorite(@Body params: PageParams): BasePageResponse<TangPoetry>

2、Repository

package com.co.post.data.repositoryimport com.co.post.data.api.ApiService
import com.co.post.data.model.PageParams
import com.co.post.data.model.db.TangPoetry
import com.co.post.data.model.getDataIfSuccess
import com.co.post.data.model.success/***    @Author : Cook*    @Date   : 2025/3/11*    @Desc   :*    @Version:*/
class PoetryRepository(private val apiService: ApiService) {suspend fun queryFavorite(pageNum: Int): Pair<Boolean, List<TangPoetry>> {val result = apiService.queryFavorite(PageParams(pageNum))val data = result.getDataIfSuccess() ?: emptyList()return Pair(result.data.isLastPage, data)}}

3、定义PagingSource

实现

getRefreshKey和load方法

getRefreshKey获取刷新的参数,可以自定义

load方法处理加载逻辑

nextKey参数为null时代表加载完成

package com.co.post.data.repository.pagingimport androidx.paging.PagingSource
import androidx.paging.PagingState
import com.co.post.data.model.db.TangPoetry
import com.co.post.data.repository.PoetryRepository/***    @Author : Cook*    @Date   : 2025/3/11*    @Desc   :*    @Version:*/
class PoetryPagingSource(private val repository: PoetryRepository) :PagingSource<Int, TangPoetry>() {override fun getRefreshKey(state: PagingState<Int, TangPoetry>): Int? {return 1}override suspend fun load(params: LoadParams<Int>): LoadResult<Int, TangPoetry> {return try {val page = params.key ?: 1val pair = repository.queryFavorite(page)LoadResult.Page(data = pair.second,prevKey = if (page == 1) null else page - 1,nextKey = if (pair.first) null else page + 1)} catch (e: Exception) {LoadResult.Error(e)}}}

4、ViewModel中调用

package com.co.post.ui.page.homeimport androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import com.co.post.base.BaseViewModel
import com.co.post.data.model.db.TangPoetry
import com.co.post.data.repository.PoetryRepository
import com.co.post.data.repository.paging.PoetryPagingSource
import com.co.post.utils.CacheHelper
import com.co.post.utils.Constants
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch/***    @Author : Cook*    @Date   : 2025/1/2*    @Desc   :*    @Version:*/
class HomeViewModel(private val repository: PoetryRepository
) : BaseViewModel() {var viewStates by mutableStateOf(HomeViewState())private setprivate var pagingSource: PoetryPagingSource? = nullval tangPoetryPage = Pager(config = PagingConfig(pageSize = Constants.PAGE_SIZE,enablePlaceholders = false),pagingSourceFactory = {PoetryPagingSource(repository).also {pagingSource = it}}).flow.cachedIn(viewModelScope)fun dispatch(action: HomeViewAction) {when (action) {is HomeViewAction.Init -> init()is HomeViewAction.UnCollect -> unCollect(action.id)}}private fun unCollect(id: String) {viewModelScope.launch {flow {emit(repository.unCollectPoetry(id))}.catch {mViewEvents.send(CommonViewEvent.ErrorMessage(it.message ?: ""))}.collect { data ->val result = if (data) {pagingSource?.invalidate()"操作成功"} else {"操作失败"}mViewEvents.send(CommonViewEvent.SuccessMessage(result))}}}private fun init() {viewStates = viewStates.copy(isLogged = CacheHelper.isLogged())}}data class HomeViewState(val tangPoetryList: List<TangPoetry> = emptyList(),val isLogged: Boolean = CacheHelper.isLogged(),)sealed class HomeViewAction {object Init : HomeViewAction()data class UnCollect(val id: String) : HomeViewAction()
}

5、View中使用

package com.co.post.ui.page.homeimport androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.pulltorefresh.PullToRefreshDefaults
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavHostController
import androidx.paging.LoadState
import androidx.paging.compose.collectAsLazyPagingItems
import com.co.post.base.BaseViewModel.CommonViewEvent
import com.co.post.ui.page.common.Screen
import com.co.post.ui.theme.commonTopBarText
import com.co.post.ui.theme.themeColor
import com.co.post.ui.theme.white
import com.co.post.ui.theme.white10
import com.co.post.ui.widgets.CommonDevider
import com.co.post.ui.widgets.ErrorItem
import com.co.post.ui.widgets.LoadingItem
import com.co.post.ui.widgets.NoMoreItem
import com.co.post.ui.widgets.SNACK_ERROR
import com.co.post.ui.widgets.SNACK_SUCCESS
import com.co.post.ui.widgets.SelectOptionDialog
import com.co.post.ui.widgets.popupSnackBar
import com.co.post.utils.CacheHelper
import com.co.post.utils.PostAppUtils
import com.co.post.utils.PostAppUtils.getFirstPoetry
import com.co.post.utils.RouteUtils
import org.koin.androidx.compose.koinViewModel/***    @Author : Cook*    @Date   : 2024/9/29*    @Desc   :*    @Version:*/@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeScreen(navController: NavHostController,snackBarHostState: SnackbarHostState,innerPadding: PaddingValues,viewModel: HomeViewModel = koinViewModel()
) {val dataPage = viewModel.tangPoetryPage.collectAsLazyPagingItems()val refreshState = dataPage.loadState.refresh is LoadState.Loadingvar showOption by remember { mutableStateOf(false) }var clickID by remember { mutableStateOf("") }var isLogged by remember { mutableStateOf(CacheHelper.isLogged()) }val coroutineState = rememberCoroutineScope()val pullRefreshState = rememberPullToRefreshState()val listState: LazyListState = rememberLazyListState()LaunchedEffect(Unit) {viewModel.viewEvents.collect {when (it) {is CommonViewEvent.SuccessMessage -> {popupSnackBar(coroutineState,snackBarHostState,label = SNACK_SUCCESS,it.message)}is CommonViewEvent.ErrorMessage -> popupSnackBar(coroutineState,snackBarHostState,label = SNACK_ERROR,it.message)is CommonViewEvent.PopBack -> navController.popBackStack()}}}LaunchedEffect(isLogged) {if (isLogged) {dataPage.refresh()} else {navController.navigate(Screen.Login.route)}}Column(modifier = Modifier.fillMaxSize().background(themeColor).statusBarsPadding().padding(innerPadding)) {TopAppBar(title = {Row(modifier = Modifier.fillMaxWidth(),) {Text(text = "我的收藏",style = TextStyle(fontSize = commonTopBarText,color = white),modifier = Modifier.align(Alignment.CenterVertically),)}},actions = {IconButton(onClick = {RouteUtils.navTo(navCtrl = navController,destinationName = Screen.PoetrySearch.route,isLaunchSingleTop = true)}) {Icon(imageVector = Icons.Default.Search,contentDescription = "搜索",tint = white)}},modifier = Modifier.fillMaxWidth(),colors = TopAppBarDefaults.topAppBarColors(containerColor = themeColor))PullToRefreshBox(isRefreshing = refreshState,onRefresh = { dataPage.refresh() },modifier = Modifier.fillMaxSize().background(white).padding(top = 12.dp),state = pullRefreshState,indicator = {PullToRefreshDefaults.Indicator(state = pullRefreshState,isRefreshing = refreshState,modifier = Modifier.align(Alignment.TopCenter),color = themeColor)}) {LazyColumn(state = listState,modifier = Modifier.fillMaxSize().padding(top = 18.dp, start = 10.dp, bottom = 18.dp, end = 10.dp).background(white10),) {items(dataPage.itemCount) { index ->val item = dataPage[index]if (item != null) {PoetryItem(item.title,item.author,getFirstPoetry(item.paragraphs), isCollect = false,{clickID = item.idshowOption = true}) {RouteUtils.navTo(navCtrl = navController,args = item.id,destinationName = Screen.PoetryDetail.route,isLaunchSingleTop = true)}CommonDevider()} else {// 显示加载中的占位符LoadingItem()}}when (dataPage.loadState.append) {is LoadState.Loading -> {item {LoadingItem()}}is LoadState.Error -> {item {ErrorItem((dataPage.loadState.append as LoadState.Error).error) {dataPage.retry()}}}is LoadState.NotLoading -> {if (dataPage.loadState.append.endOfPaginationReached) {item {NoMoreItem()}}}else -> {}}}}}SelectOptionDialog(showOption, listOf("取消收藏"), { showOption = false }) { index ->viewModel.dispatch(HomeViewAction.UnCollect(clickID))}}@Composable
fun PoetryItem(title: String,author: String,content: String,isCollect: Boolean = false,onLongClick: () -> Unit,onClick: () -> Unit,
) {Row(modifier = Modifier.fillMaxWidth().padding(12.dp).background(Color.White, shape = RoundedCornerShape(8.dp)).pointerInput(Unit) {detectTapGestures(onTap = {onClick.invoke()},onLongPress = {onLongClick.invoke()})},verticalAlignment = Alignment.CenterVertically) {Box(modifier = Modifier.size(80.dp).background(Color(PostAppUtils.getBackgroundColorId(title)),shape = RoundedCornerShape(8.dp)),contentAlignment = Alignment.Center) {Text(text = PostAppUtils.getFirstString(content),color = Color.White,fontSize = 42.sp,fontWeight = FontWeight.Bold)}Spacer(modifier = Modifier.width(10.dp))Column(modifier = Modifier.weight(1f)) {Text(text = title, fontSize = 15.sp, color = Color.Black)Spacer(modifier = Modifier.height(8.dp))Row(modifier = Modifier.fillMaxWidth(),verticalAlignment = Alignment.CenterVertically) {Text(text = author, fontSize = 13.sp, color = Color.Gray)if (isCollect) {Spacer(modifier = Modifier.weight(1f))Text(text = "已收藏",fontSize = 11.sp,color = Color.White,modifier = Modifier.background(themeColor, shape = RoundedCornerShape(4.dp)).padding(horizontal = 6.dp, vertical = 2.dp))}}Spacer(modifier = Modifier.height(5.dp))Text(text = content, fontSize = 12.sp, color = Color.Gray)}}
}

文章来源:https://blog.csdn.net/Chen_jianbo/article/details/146224675
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ppmy.cn/server/174988.html

相关文章

【病毒分析】熊猫烧香病毒分析及其查杀修复

目录 前言 一、样本概况 1.1 样本信息 1.2 测试环境及工具 1.3 分析目标 二、具体行为分析 2.1 主要行为 2.1.1 恶意程序对用户造成的危害 2.2 恶意代码分析 2.2.1 加固后的恶意代码树结构图(是否有加固) 2.2.2 恶意程序的代码分析片段 三、解决方案(或总结) 3.1 …

MySQL数据库复杂的增删改查操作

在前面的文章中&#xff0c;我们主要学习了数据库的基础知识以及基本的增删改查的操作。接下去将以一个比较实际的公司数据库为例子&#xff0c;进行讲解一些较为复杂且现时需求的例子。 基础知识&#xff1a; 一文清晰梳理Mysql 数据库基础知识_字段变动如何梳理清楚-CSDN博…

K8S自动扩缩容实践

以下是 Kubernetes 中 Horizontal Pod Autoscaler (HPA) 和 Vertical Pod Autoscaler (VPA) 的详细 YAML 配置过程及说明&#xff1a; 一、Horizontal Pod Autoscaler (HPA) 1. 前提条件&#xff1a;安装 Metrics Server HPA 依赖资源指标&#xff08;如 CPU/内存&#xff09…

Linux下部署前后端分离项目 —— Linux下安装nginx

1 打包前后端项目 1.1 打包Vue项目 # 构建生产环境包 npm run build:prod 注意&#xff1a;我这边使用的命令是 npm run build:pro&#xff0c;一般都是 npm run build:prod&#xff0c;具体看前端package.json文件中是如何配置的&#xff0c;如下&#xff1a; 1.2 后端打包 …

深入解析 React 最新特性:革新、应用与最佳实践

深入解析 React 最新特性&#xff1a;革新、应用与最佳实践 1. 引言 React 作为前端开发的核心技术之一&#xff0c;近年来不断推出 新的 API 和优化机制&#xff0c;从 Concurrent Rendering&#xff08;并发模式&#xff09; 到 Server Components&#xff08;服务器组件&a…

c++介绍函数指针 十

指针代表内存中地址标识符&#xff0c;变量&#xff0c;数组都是存储内存中的数据。所以可以获得它们的地址&#xff0c;用指针来表示这块内存。 如图输出内存中的地址。 对于一个函数来说&#xff0c;也是内存中存储这段数据&#xff0c;所以我们也可以获取函数的地址。 函数…

Mysql表的查询

一&#xff1a;创建一个新的数据库&#xff08;companydb),并查看数据库。 二&#xff1a;使用该数据库&#xff0c;并创建表worker。 mysql> use companydb;mysql> CREATE TABLE worker(-> 部门号 INT(11) NOT NULL,-> 职工号 INT(11) NOT NULL,-> 工作时间 D…

unity几种设计模式(自用)

unity的几种设计模式 1.单例模式2.观察者模式3.工厂模式4.状态模式5.命令模式 1.单例模式 public class GameManager : MonoBehaviour {public static GameManager Instance { get; private set; }void Awake(){if (Instance null){Instance this;DontDestroyOnLoad(gameObj…