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

在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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

reactor (百万并发服务器) -- 1

为了从点滴开始&#xff0c;文章会先从一些基础socket去补充一些经常发生但是没有很深入去思考的细节。然后我们再开始去设计reactor的设计&#xff0c;可以选择跳过起过前面部分。 为了能从0开始去设计&#xff0c;测试&#xff0c;优化...整个过程会分为2-3篇文章输出&#x…

Redis队列Stream

1 缘起 项目中处理文件的场景&#xff1a; 将文件处理请求放入队列&#xff0c; 一方面&#xff0c;缓解服务器文件处理压力&#xff1b; 另一方面&#xff0c;可以根据文件大小拆分到不同的队列&#xff0c;提高文件处理效率。 这是Java开发组Leader佳汇提出的文件处理方案&a…

网络安全—小白自学

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成熟…

QT实现用本地资源管理器来打开文件夹

QString path"文件夹路径";QDesktopServices::openUrl(QUrl("file:"path, QUrl::TolerantMode)); 在windows中QT编程&#xff0c;使用资源管理器来打开指定本地文件夹的方法&#xff1a; 第一种&#xff1a;使用Qprocess命令&#xff08;相当于在cmd命令管…

测试C#调用Aplayer播放视频(2:VideoPlayer源码学习)

参考文献1除了介绍Aplayer组件的用法之外&#xff0c;还提供有demo下载以供学习&#xff0c;本文学习并记录其中的使用方式。   VideoPlayer项目使用C#在VS2013开发&#xff0c;其解决方案中包括VideoPlayer和VideoPlayer两个小项目&#xff0c;前者基于.net framework4.0&am…

【数据分析】上市公司半年报数据分析

前言 前文介绍过使用网络技术获取上市公司半年报数据的方法&#xff0c;本文将对获取到的数据进行简要的数据分析。 获取数据的代码介绍在下面的两篇文章中 【java爬虫】使用selenium获取某交易所公司半年报数据-CSDN博客 【java爬虫】公司半年报数据展示-CSDN博客 全量数…

C#开发DLL,CAPL调用(CAPL>> .NET DLL)

文章目录 展示说明新建类库工程C# 代码生成dllCAPL脚本调用dll,输出结果展示 ret为dll里函数返回的值。 说明 新建类库工程 在visual studio中建立。 C# 代码 using

Python构造代理IP池提高访问量

目录 前言 一、代理IP是什么 二、代理IP池是什么 三、如何构建代理 IP 池 1. 从网上获取代理 IP 地址 2. 对 IP 地址进行筛选 3. 使用筛选出来的 IP 地址进行数据的爬取 四、总结 前言 爬虫程序是批量获取互联网上的信息的重要工具&#xff0c;在访问目标网站时需要频…

启动Vue项目报错Error: error:0308010C:digital envelope routines::unsupported

问题描述 启动Vue项目报错Error: error:0308010C:digital envelope routines::unsupported 出现这个一般就是node版本的问题&#xff0c;通过命令查看node -v查看node版本&#xff1b; 百度查了好多&#xff0c;都让我降低node版本&#xff0c;属实太麻烦了 在不改node版本的…

[论文笔记]BGE

引言 今天介绍论文BGE,是智源开源的语义向量模型,BAAI General Embedding。 作者发布了C-Pack,一套显著推进中文嵌入领域的资源包。包括三个重要资源: 1) C-MTEB是一个全面的中文文本嵌入基准,涵盖了6个任务和35个数据集。 2) C-MTP是一个从标记和未标记的中文语料库中选…

均值、方差、标准差

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

c++11新特性

文章目录 1. C11简介2. 统一的列表初始化2.1 &#xff5b;&#xff5d;初始化2.2 std::initializer_list 3. 声明3.1 auto3.2 decltype3.3 nullptr 4 范围for循环5. STL中一些变化 1. C11简介 2003年&#xff0c;C标准委员会提交了技术勘误表&#xff08;TC1&#xff09;&…

Python的错误和异常处理

一、错误和异常 编程中出现的错误大致可以分为两类&#xff1a;错误和异常。 (一)错误 错误又可以分为两类&#xff1a;语法错误和逻辑错误。 1. 语法错误 语法错误又称解析错误&#xff0c;它是指在编写程序时&#xff0c;程序的语法不符合Python语言的规范&#xff0c;导致…

BI零售数据分析,告别拖延症,及时掌握一线信息

在日常的零售数据分析中&#xff0c;经常会因为数据量太大&#xff0c;分析指标太多且计算组合多变而导致数据分析报表难产&#xff0c;零售运营决策被迫拖延症。随着BI数据可视化分析技术的发展&#xff0c;智能化、可视化、自助分析的BI数据分析逐渐成熟&#xff0c;形成一套…

如何使用navicat图形化工具远程连接MariaDB数据库【cpolar内网穿透】

公网远程连接MariaDB数据库【cpolar内网穿透】 文章目录 公网远程连接MariaDB数据库【cpolar内网穿透】1. 配置MariaDB数据库1.1 安装MariaDB数据库1.2 测试局域网内远程连接 2. 内网穿透2.1 创建隧道映射2.2 测试随机地址公网远程访问3. 配置固定TCP端口地址3.1 保留一个固定的…

Vue项目搭建及使用vue-cli创建项目、创建登录页面、与后台进行交互,以及安装和使用axios、qs和vue-axios

目录 1. 搭建项目 1.1 使用vue-cli创建项目 1.2 通过npm安装element-ui 1.3 导入组件 2 创建登录页面 2.1 创建登录组件 2.2 引入css&#xff08;css.txt&#xff09; 2.3 配置路由 2.5 运行效果 3. 后台交互 3.1 引入axios 3.2 axios/qs/vue-axios安装与使用 3.2…

Webpack常见的插件和模式

文章目录 一、认识插件Plugin1.认识Plugin 二、CleanWebpackPlugin三、HtmlWebpackPlugin1.生成index.html分析2.自定义HTML模板3.自定义模板数据填充 四、DefinePlugin1.DefinePlugin的介绍2.DefinePlugin的使用 五、Mode配置 一、认识插件Plugin 1.认识Plugin Webpack的另一…

【HMS Core】机器学习服务热门问题合集

【关键词】 机器学习服务、文本识别、身份证识别 【问题描述1】 机器学习服务的文本识别能力&#xff0c;是否支持草书等&#xff1f; 【解决方案】 草书是不支持的&#xff0c;目前建议使用较为规范的字体测试。 【问题描述2】 机器学习服务是否支持训练模型&#xff1f;…

Flink on yarn 加载失败plugins失效问题解决

Flink on yarn 加载失败plugins失效问题解决 flink版本&#xff1a;1.13.6 1. 问题 flink 任务运行在yarn集群,plugins加载失效,导致通过扩展资源获取任务参数失效 2. 问题定位 yarn容器的jar包及插件信息,jar包是正常上传 源码定位 加载plugins入口&#xff0c;TaskMana…

Mysql权限控制语句

1.创建用户 create user ky32localhost IDENTIFIED by 123456 create user&#xff1a;创建用户开头 ky32&#xff1a;用户名 localhost 新建的用户可以在哪些主机上登录 即可以使用ip地址&#xff0c;网段主机名 ky32localhost ky32192.168.233.22 ky32192.168.233.0/2…