在Jetpack Compose中使用Paging 3实现无限滚动

news2025/7/28 3:46:33

在Jetpack Compose中使用Paging 3实现无限滚动

本文将介绍在Jetpack Compose中进行分页加载。分页加载意味着一次只加载应用程序中的小数据块。
假设您在服务器上有大量数据,并且您希望在UI上显示这些数据。显然,您不希望一次性加载所有数据。您希望每次只加载少量数据。
这就是分页的作用,当您在应用程序中向上滚动时,它会加载下一组数据,通过这种方式可以提高应用程序的性能。

基本上,我们将构建一个非常简单的应用程序,通过使用paging 3库在RecyclerView上显示狗的图像。最好的部分是您可以进行无限滚动,而不必担心应用程序的性能问题。

如上所示,在上面的视频中,我们正在RecyclerView上显示狗的图像,最好的部分是当我们向下滚动时,图像是动态加载的。

//Dependencies
val daggerHilt = "2.47"
val coroutine = "1.7.1"

//dagger hilt
implementation("com.google.dagger:hilt-android:$daggerHilt")
kapt("com.google.dagger:hilt-android-compiler:$daggerHilt")
implementation("androidx.hilt:hilt-navigation-compose:1.1.0-alpha01")

//coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine")
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.7.0-alpha02")

//paging 3
implementation ( "androidx.paging:paging-runtime-ktx:3.2.1")
implementation ("androidx.paging:paging-compose:3.3.0-alpha02")


//retrofit
implementation ("com.squareup.retrofit2:retrofit:2.9.0")
//moshi
implementation("com.squareup.moshi:moshi-kotlin:1.12.0")
implementation ("com.squareup.retrofit2:converter-moshi:2.9.0")

//coil
implementation("io.coil-kt:coil-compose:2.4.0")

这些是我们用于构建此应用程序的依赖项。确保您已经了解dagger hilt、coroutines和retrofit,因为我们只会谈论Paging 3。

Model

data class DogsModel(
  val id:String,
  val url:String
)

首先,我们创建了一个DogsModel文件,它只是一个模式,以便我们可以将其中的数据绑定在一起。

interface ApiService {

    companion object {
        const val BASE_URL = "https://api.thedogapi.com"
    }

    @GET("v1/images/search")
    suspend fun getAllDogs(
        @Query("page") page: Int,
        @Query("limit") limit: Int
    ): List<DogsModel>
}

如您所见,在上面的代码中,我们使用https://api.thedogapi.com API获取狗的图像。

请注意查询参数中,我们传递了页面和限制。当您向下滚动时,页码将增加,而限制将是您想要一次加载的图像数量。

Repository

class DogsRepository @Inject constructor(
    private val apiService:ApiService
) {
    suspend fun getDogs(
        page:Int,
        limit:Int
    ):List<DogsModel> = apiService.getAllDogs(
        page,limit
    )
}

之后,我们创建了一个存储库,在其中编写从服务器获取数据的逻辑。

正如您在上面的图像中所注意到的那样,在创建存储库之后,我们必须创建一个PagingSource类,在其中编写我们的分页逻辑。

忘记RemoteMediator,它用于缓存我们在本文中未使用的数据。

class DogsPagingSource @Inject constructor(
    private val repository:DogsRepository
) :PagingSource<Int,DogsModel>() {

    override fun getRefreshKey(state:PagingState<Int,DogsModel> ):Int?= state.anchorPosition

    override suspend fun load(params:LoadParams<Int> ) :LoadResult<Int,DogsModel> {
        val page = params.key?1
        val response = repository.getDogs(page,params.loadSize )
        return try {
            LoadResult.Page(
                data=response,
                prevKey=if (page == 1) null else page.minus(1),
                nextKey=if (response.isEmpty()) null else page.plus(1)
            )
        } catch (e: IOException) {
            LoadResult.Error(
                e
            )
        } catch (e: HttpException) {
            LoadResult.Error(
                e
            )
        }
    }
}

如您所见,我们创建了一个DogsPagingSource类,该类扩展了PagingSource<Int,DogsModel>()类,这将有助于编写分页逻辑。
<Int,DogsModels>()中的Int表示它将以Int值作为页码输入,DogsModel是服务器响应。
它将覆盖两个重要函数,getRefreshKey函数将帮助您在要刷新数据时使用,它还会获取有关当前加载数据的信息。
load()这里我们将编写实际的分页逻辑。
正如您注意到的,它返回LoadResult,它是包含PageError子数据类的密封类。
在成功时,我们将返回Page,否则为Error。
使用LoadParams,我们将找到页面编号和限制,如果无法找到页面编号,则将其传递为1。
LoadResult.Page中,我们传递响应(从服务器返回的任何结果)。
prevKey中,如果仅有一页可用,则传递null,否则在向上滚动时减少页面编号。
nextKey中,如果响应为空,则表示没有可用页面,否则在向下滚动时增加页面编号。
在向上滚动的情况下,我们将减少页面编号,在向下滚动的情况下,我们将增加页面编号。

ViewModel


@HiltViewModel
class DogsViewModel @Inject constructor(
    private val dogsPagingSource: DogsPagingSource
) : ViewModel() {

    private val _dogResponse: MutableStateFlow<PagingData<DogsModel>> =
        MutableStateFlow(PagingData.empty())
    var dogResponse = _dogResponse.asStateFlow()
        private set

    init {
        viewModelScope.launch {
            Pager(
                config = PagingConfig(
                    10, enablePlaceholders = true
                )
            ) {
                dogsPagingSource
            }.flow.cachedIn(viewModelScope).collect {
                _dogResponse.value = it
            }
        }
    }
}

在上面的代码中,我们创建了一个ViewModel,_dogResponse变量的类型是PagingData<T>,它存储来自DogsPagingSource的结果。

init{}块中,我们调用了Pager,它将limitPagingSource作为必要参数,并返回结果。

让我们在UI上收集这个结果。

@Composable
fun DogsScreen(
    modifier: Modifier = Modifier,
    viewModel: DogsViewModel = hiltViewModel()
) {
    val response = viewModel.dogResponse.collectAsLazyPagingItems()

    LazyVerticalStaggeredGrid(
        columns = StaggeredGridCells.Fixed(3),
        modifier = modifier.fillMaxSize()
    ) {

        items(response.itemCount) {
            AsyncImage(
                model = ImageRequest.Builder(LocalContext.current)
                    .data(response[it]?.url ?: "-")
                    .crossfade(true)
                    .build(),
                placeholder = painterResource(R.drawable.ic_launcher_foreground),
                contentDescription = "",
                contentScale = ContentScale.Crop,
                modifier = Modifier
                    .padding(20.dp)
                    .clip(CircleShape)
            )
        }

        response.apply {
            when {
                loadState.refresh is LoadState.Loading || loadState.append is LoadState.Loading -> {
                    item {
                        Box(
                            modifier = Modifier.fillMaxWidth(),
                            contentAlignment = Alignment.Center
                        ) {
                            CircularProgressIndicator(
                                modifier = Modifier.align(Alignment.Center)
                            )
                        }
                    }
                }

                loadState.refresh is LoadState.Error || loadState.append is LoadState.Error -> {
                    item {
                        Text(text = "Error")
                    }
                }

                loadState.refresh is LoadState.NotLoading -> {
                }
            }
        }
    }

}

如您所见,首先我们创建了ViewModel的对象,通过collectAsLazyPagingItems()来收集响应。
我们使用LazyVerticalStaggeredGrid来通过Coil库显示图像。
这里的load.refresh表示当您首次加载/出错数据时,而load.append表示当您向下滚动时追加加载/出错数据。

Github

https://github.com/nameisjayant/compose-blogs-repository/tree/main/app/src/main/java/com/nameisjayant/articlesrepository/ui/paging3

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker(1)——安装Docker以及配置阿里云镜像加速

目录 一、简介 二、安装Docker 1. 访问Docker官网 2. 卸载旧版本Dokcer 3. 下载yum-utils&#xff08;yum工具包集合&#xff09; 4. 设置国内镜像仓库 5. 更新yum软件包索引 6. 安装Docker 7. 启动Docker 8. 卸载Docker 三、阿里云镜像加速 1. 访问阿里云官网 2. …

C++——类和对象之拷贝构造

拷贝构造 本章思维导图&#xff1a; 注&#xff1a;本章思维导图对应的xmind文件和.png文件都已同步上传到”资源“ 如果我们想要用一个已经存在的对象实例化一个与之完全相同的对象&#xff0c;怎么做呢&#xff1f; C提供了一个简单的方法——拷贝构造 拷贝构造是C类里面默…

如何利用python连接讯飞的星火大语言模型

星火大模型是科大讯飞推出的一款人工智能语言模型&#xff0c;它采用了华为的昇腾910 AI处理器。这款处理器是一款人工智能处理器&#xff0c;具有强大的计算能力和高效的能耗控制能力。 华为昇腾910 AI处理器采用了创新的Da Vinci架构&#xff0c;这种架构在设计上充分考虑了…

均值、方差、标准差

1 中间值和均值 表现&#xff02;中间值&#xff02;的统计名词&#xff1a; a.均值:   mean&#xff0c;数列的算术平均值&#xff0c;反应了数列的集中趋势,等于有效数值的合除以有效数值的个数&#xff0e;b.中位值:  median&#xff0c;等于排序后中间位置的值&#x…

工会排队营销玩法,让消费者乐于参与其中

小编介绍&#xff1a;10年专注商业模式设计及软件开发&#xff0c;擅长企业生态商业模式&#xff0c;商业零售会员增长裂变模式策划、商业闭环模式设计及方案落地&#xff1b;扶持10余个电商平台做到营收过千万&#xff0c;数百个平台达到百万会员&#xff0c;欢迎咨询。 工会…

DC/DC 隔离模块MGS102405、MGS102412、MGS60505、MGS62405、MGS62415直流转换器 Module

概述 MG DC-DC转换器采用行业标准尺寸&#xff0c;包括SIP6、SIP8、1 “ X 1 ”和1 “ X 2 ”。这些模块具有DC4.5至13V/DC9至36V/DC18至76V的宽输入范围和DC1500V&#xff08;1分钟&#xff09;的隔离电压。其他功能包括内置过流保护电路&#xff08;自动恢复&#xff09;、内…

PO- Target XSD requires a value错误处理

问题描述&#xff1a; . Values missing in queue context. Target XSD requires a value forhis element. but the taroet-field mappina does not create one. 原因分析&#xff1a; Xsd即DT、MT对应的字段&#xff0c;上面没有具体写那个字段&#xff0c;但可以判断是消息…

对象存储那点事

在很长的一段时间里&#xff0c;DAS、SAN 和 NAS 这三种架构几乎统治了数据存储市场。所有行业用户的数据存储需求&#xff0c;都是在这三者中进行选择。 然而&#xff0c;随着时代的发展&#xff0c;一种新的数据存储形态诞生&#xff0c;开始挑战前面三者的垄断地位。没错&am…

0基础学习PyFlink——时间滚动窗口(Tumbling Time Windows)

大纲 mapreduce完整代码参考资料 在《0基础学习PyFlink——个数滚动窗口(Tumbling Count Windows)》一文中&#xff0c;我们发现如果窗口内元素个数没有达到窗口大小时&#xff0c;计算个数的函数是不会被调用的。如下图中红色部分 那么有没有办法让上图中&#xff08;B,2&…

一次不接受ElasticSearch官方建议导致的事故

记录一下 一次Elasticsearch集群事故分析、排查、处理 背景介绍 事故发生的ElasticSearch集群共有7台机器&#xff1a; 127.0.204.193127.0.204.194127.0.204.195127.0.220.73127.0.220.74127.0.220.220127.0.220.221 其中193、194、195的机器配置一样&#xff0c;具体如下&…

百度地图直接用的封装好的--自用vue的(每次项目都要有百度地图,还是搞个封装的差不多的以后可以直接拿来用)

自用的封装好的,有弹窗,轨迹回放,画点画地图 完整代码使用 百度地图的官方文档 百度地图必须的三个引用 完整代码 <template><AButton style"background-color: #3ba7ea;color: white;width: 100px;float: right" click"buttonClick">轨迹回放…

图书馆书目推荐数据分析与可视化

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性&#xff1a;…

瑞萨e2studio(28)----SPI 驱动WS2812灯珠

瑞萨e2studio.28--SPI 驱动WS2812灯珠 概述视频教学样品申请芯片级联方法数据传输时序新建工程软件准备保存工程路径芯片配置开始SPI配置SPI属性配置时钟配置SPI配置CPHA配置代码hal_entry.cws2812.cws2812.h 概述 本文介绍了如何使用瑞萨RA微控制器&#xff0c;结合E2STUDIO…

基于热交换算法的无人机航迹规划-附代码

基于热交换算法的无人机航迹规划 文章目录 基于热交换算法的无人机航迹规划1.热交换搜索算法2.无人机飞行环境建模3.无人机航迹规划建模4.实验结果4.1地图创建4.2 航迹规划 5.参考文献6.Matlab代码 摘要&#xff1a;本文主要介绍利用热交换算法来优化无人机航迹规划。 1.热交换…

【设计模式】第22节:行为型模式之“状态模式”

一、简介 状态模式一般用来实现状态机&#xff0c;而状态机常用在游戏、工作流引擎等系统开发中。不过&#xff0c;状态机的实现方式有多种&#xff0c;除了状态模式&#xff0c;比较常用的还有分支逻辑法和查表法。该模式允许对象内部状态改变使改变它的行为。 二、适用场景…

【NI-DAQmx入门】计数器

1.计数器的作用 NI产品的计数器一般来说兼容TTL信号&#xff0c;定义如下&#xff1a;0-0.8V为逻辑低电平&#xff0c;2~5V为高电平&#xff0c;0.8-2V为高阻态&#xff0c;最大上升下降时间为50ns。 计数器可以感测上升沿&#xff08;从逻辑低到逻辑高的转变&#xff09;和下降…

【电源专题】POE连接方式与功率等级划分

在文章【电源专题】什么是POE&#xff1f;中我们讲到了&#xff1a;PoE&#xff08;Power over Ethernet&#xff09;是指通过网线传输电力的一种技术&#xff0c;借助现有以太网口通过网线同时为终端设备&#xff08;如&#xff1a;IP电话、AP、IP摄像头等&#xff09;进行数据…

web:[GYCTF2020]Blacklist

题目 点开靶机&#xff0c;页面显示为 查看源码 没有其他线索 先提交1试一下 猜测是sql注入&#xff0c;先测试 同时注意到url 提交为3-1&#xff0c;发现页面回显为空白 可以判断为字符型注入 输入select&#xff0c;看是否存在回显 回显了黑名单限制的关键字 但是发现没有…

第五章 I/O管理 九、磁盘的结构

目录 一、概念 二、磁盘的物理地址 1、定义&#xff1a; 2、图像&#xff1a; 如何读取一个“块”&#xff1a; 三、磁盘的分类 四、总结 一、概念 磁盘是由多个盘片和读写磁头组成的&#xff0c;每个盘片都有自己的读写磁头。盘片表面被划分成许多同心圆的磁道&#xff…

并发编程-CPU缓存架构详解 Disruptor的高性能设计方案

1.CPU缓存架构详解 1.1 CPU高速缓存概念 CPU缓存即高速缓冲存储器&#xff0c;是位于CPU与主内存间的一种容量较小但速度很高的存储器。CPU高 速缓存可以分为一级缓存&#xff0c;二级缓存&#xff0c;部分高端CPU还具有三级缓存&#xff0c;每一级缓存中所储存的全部数 据都…