当前位置: 首页 > news >正文

Jetpack Compose:使用PagerIndicator和Infinity实现滚动的HorizontalPager

Jetpack Compose:使用PagerIndicator和Infinity实现滚动的HorizontalPager

Android | Kotlin | Jetpack Compose | ViewPager | PagerIndicator
可能你已经知道,Jetpack Compose 默认不包含内置的ViewPager组件。然而,我们可以通过在 build.gradle 文件中添加 accompanist 库依赖,将 ViewPager 功能集成到我们的项目中。

implementation "com.google.accompanist:accompanist-pager:0.28.0"

为了将指示器纳入其中,我们还将利用accompanist库。

implementation "com.google.accompanist:accompanist-pager-indicators:0.28.0"

注意:对于此项目,我们使用的是Compose版本1.3.1和Kotlin版本1.8.10。
在这里插入图片描述

让我们从创建一个HorizontalPager 开始

accompanist库的存在,创建HorizontalPager是一项简单的任务。

HorizontalPager(count = pageCount,state = pagerState,modifier = modifier
) {// page content
}
  • Count:页面数
    我们将计数设置为非常大的数,如Int.MAX_VALUE,这样我们就可以实现无限滚动行为。
// Used Int.MAX_VALUE for infinity scroll
val pageCount = Int.MAX_VALUE
  • State:用于控制或观察分页器状态的状态对象。
    对于状态创建,我们只需要 initialPage 值。为了实现双向无限滚动,我们应该从给定页面计数的中间开始。因此,initialPage 可以设置如下示例:
val middlePage = pageCount / 2
val pagerState = rememberPagerState(initialPage = middlePage)

尽管一开始所有东西似乎都是正确的,但我们很快就会遇到一个涉及最初显示页面的问题。

技巧

为了实现ViewPager的无限行为,我们通过设置计数为一个非常大的数字来实现了一种解决方案。但是,我们实际的物品列表(奖杯)要小得多。为了确保ViewPager显示我们真正列表中的正确页面而不创建重复页面,我们需要适当地处理页面编号。

为了解决这个挑战,我们将提供的页面编号除以奖杯列表的大小。这个除法允许我们在我们的真实列表中获取正确的页面索引。通过执行这个计算,我们确保ViewPager只显示列表中的实际物品,防止任何重复。

通过利用这种方法,我们可以在ViewPager中轻松导航通过奖杯列表,同时保持其无限行为。

看下面的例子:

val realSize = trophies.sizeHorizontalPager(count = pageCount,state = pagerState,modifier = modifier
) { page -> val realPage = page % realSize// max value is trophies.sizeTrophyWidget(realPage, trophy = trophies[realPage])
}

你懂了吗?不懂?(那我们就来算一下吧!)
page count

看起来我们的数学运算正常运行!

如果您仔细观察,就会发现初始页面不是奖杯列表中的第一个。实际上,初始状态取决于奖杯列表的大小。为了解决这个差异并确保正确的初始状态,有必要计算并传递一个参数到ViewPager状态。

val realSize = trophies.sizeval middlePage = pageCount / 2
// Init the PagerState with a very large number and make it always start from the first item of the real list
val pagerState = rememberPagerState(initialPage = middlePage - (middlePage % realSize))

通过将middlePage减去middlePage与奖杯数量取余的结果,确保ViewPager将从奖杯列表的开头开始。

页面指示器

添加指示器也很简单,我们只需要添加HorizontalPagerIndicator并将pagerState作为参数传递即可。

Android | HorizontalPager | Indicator
然而,这里存在一个问题!如果您尝试在不指定列表(奖杯)的实际大小的情况下使用pagerState,则应用程序将会空白页。那是因为HorizontalPagerIndicator的默认pageCount设置为PagerState.pageCount的值,而在我们的情况下,这是一个非常大的数。

幸运的是,我们可以通过将pageCount作为参数添加到HorizontalPagerIndicator中来指定pageCount

看一个例子:

HorizontalPagerIndicator(pagerState = pagerState,pageCount = realSize,pageIndexMapping = { it % realSize },activeColor = Color.White,modifier = modifier.align(Alignment.BottomCenter).padding(bottom = 12.dp)
)

我们还必须描述如何通过将页面传递给pageIndexMapping函数来获取活动指示器的位置。这可以通过将pagerState.currentPage除以奖杯列表的大小来实现。

如上例所示,您可以实现以下代码段:

pageIndexMapping = { currentPage % realSize }

要获取活动指示器的位置,您可以使用pageIndexMapping函数,并使用pagerState.currentPage和奖杯列表的大小执行模运算。

自动滚动

如果您还需要您的页面自动滚动,可以使用以下代码片段:

// Start auto-scroll effect
LaunchedEffect(isDraggedState) {// convert compose state into flowsnapshotFlow { isDraggedState.value }.collectLatest { isDragged ->// if not isDragged start slide animationif (!isDragged) {// infinity loopwhile (true) {// duration before each scroll animationdelay(5_000L)runCatching {pagerState.animateScrollToPage(pagerState.currentPage.inc() % pagerState.pageCount)}}}}
}

完整代码如下:

private const val SCROLL_ANIMATION_DURATION = 5_000L@OptIn(ExperimentalPagerApi::class)
@Composable
fun InfinityHorizontalPager(modifier: Modifier = Modifier) {Column(verticalArrangement = Arrangement.Center,horizontalAlignment = Alignment.CenterHorizontally) {Box(modifier = modifier.fillMaxWidth().height(400.dp)) {// Used Int.MAX_VALUE for infinity scrollval pageCount = Int.MAX_VALUE// The actual view pager size (for the HorizontalPagerIndicator)val realSize = trophies.size// Start from the middle in order to the infinity scroll for both sidesval middlePage = pageCount / 2// Init the PagerState with a very large number and make it always start from the first item of the real listval pagerState = rememberPagerState(initialPage = middlePage - (middlePage % realSize))val isDraggedState = pagerState.interactionSource.collectIsDraggedAsState()HorizontalPager(count = pageCount,state = pagerState,modifier = modifier.fillMaxWidth().fillMaxHeight().background(MaterialTheme.colors.background),) {val page = it % realSize// max value is trophies.sizeTrophyWidget(page, trophy = trophies[page])}Surface(modifier = Modifier.padding(bottom = 8.dp).align(Alignment.BottomCenter),shape = CircleShape,color = Color.Black.copy(alpha = 0.5f)) {HorizontalPagerIndicator(pagerState = pagerState,pageCount = realSize,pageIndexMapping = { it % realSize },activeColor = Color.White,modifier = Modifier.padding(horizontal = 8.dp, vertical = 6.dp))}// Start auto-scroll effectLaunchedEffect(isDraggedState) {// convert compose state into flowsnapshotFlow { isDraggedState.value }.collectLatest { isDragged ->// if not isDragged start slide animationif (!isDragged) {// infinity loopwhile (true) {// duration before each scroll animationdelay(SCROLL_ANIMATION_DURATION)runCatching {pagerState.animateScrollToPage(pagerState.currentPage.inc() % pagerState.pageCount)}}}}}}}
}@Composable
fun TrophyWidget(page: Int,trophy: TrophyCard,modifier: Modifier = Modifier
) {Box(modifier = modifier.padding(horizontal = 16.dp).fillMaxWidth().fillMaxHeight().background(Color.Black).clip(shape = RoundedCornerShape(size = 12.dp)),) {AsyncImage(model = ImageRequest.Builder(LocalContext.current).data(trophy.image).crossfade(true).build(),modifier = modifier.fillMaxWidth().fillMaxHeight().clip(shape = RoundedCornerShape(size = 12.dp)),contentDescription = null,contentScale = ContentScale.FillBounds)Column(modifier = Modifier.fillMaxWidth().background(color = Color.Black.copy(alpha = 0.5f)).padding(10.dp).align(Alignment.BottomStart)) {Text(text = trophy.location,color = Color.White,style = Typography.h6,textAlign = TextAlign.Center)Text(text = trophy.year,color = Color.White,style = Typography.h4,textAlign = TextAlign.Center)}Text(text = "$page",style = Typography.body1,color = Color.Black,textAlign = TextAlign.Center,modifier = Modifier.padding(10.dp).clip(shape = RoundedCornerShape(size = 4.dp)).background(Color.White).padding(10.dp).align(Alignment.BottomEnd))}
}@Preview(showBackground = true, showSystemUi = true)
@Composable
fun DefaultPreview() {InfinityPagerTheme {Surface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colors.background) {InfinityHorizontalPager()}}
}

在这里插入图片描述

相关文章:

Jetpack Compose:使用PagerIndicator和Infinity实现滚动的HorizontalPager

Jetpack Compose:使用PagerIndicator和Infinity实现滚动的HorizontalPager 可能你已经知道,Jetpack Compose 默认不包含内置的ViewPager组件。然而,我们可以通过在 build.gradle 文件中添加 accompanist 库依赖,将 ViewPager 功能…...

2023年杭州/广州/东莞/深圳软考(中/高级)认证,618报名特惠

软考是全国计算机技术与软件专业技术资格(水平)考试(简称软考)项目,是由国家人力资源和社会保障部、工业和信息化部共同组织的国家级考试,既属于国家职业资格考试,又是职称资格考试。 系统集成…...

springboot项目外卖管理 day03-公共字段填充与新增删除分类

文章目录 一、公共字段自动填充1.1、问题分析1.2、代码实现1.2.1、在实体类的属性上加入TableField注解,指定自动填充的策略1.2.2按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口1.2.3 功能…...

Nginx:Tomcat部署及优化(一)

Nginx:Rewrite 一、Tomcat介绍1.1 Tomcat 简介1.2 Tomcat 核心的组件1.2.1 什么是 servlet1.2.2 什么是 JSP 1.3 Tomcat 功能组件结构1.3.1 Container 结构分析 1.4 Tomcat 请求过程 二、Tomcat 服务部署三、Tomcat 虚拟主机配置四、Tomcat多实例部署 一、Tomcat介绍…...

Docker Swarm 集群搭建和使用 —— 筑梦之路

简单介绍 swarm 集群由管理节点(Manager)和工作节点(Worker)构成。 管理节点:主要负责整个集群的管理工作包括集群配置、服务管理等所有跟集群有关的工作。诸如监控集群状态、分发任务至工作节点等操作。 工作节点:主要负责执行运行服务的任务。 官方文档:docker swarm…...

是否需要更换CRM系统如何评估?如何确保更换成功?

很多企业在使用CRM客户管理系统的过程中,并没有达到预期的效果,甚至出现了实施失败的情况。部分企业可能会考虑更换CRM系统,以期获得更好的结果。但是,更换CRM系统是否值得呢?下面我们就来说说。 一、是否该更换CRM …...

CSDN竞赛57期题解

总结 交卷时一看才六十多分还有点吃惊,一看非编程题部分还是丢了二十分。填空题是这类竞赛最大的诟病,答案是名词的必然不唯一,答案需要计算的给定的参考答案必然计算错误,更离谱的是题目出成这样,反馈后官方竟然一点…...

springboot+vue.js大学生竞赛报名作品评分管理系统

本文介绍了大学生竞赛管理系统的开发全过程。通过分析大学生竞赛管理系统管理的不足,创建了一个计算机管理大学生竞赛管理系统的方案。文章介绍了大学生竞赛管理系统的系统分析部分,包括可行性分析等,系统设计部分主要介绍了系统功能设计和数…...

Python爱好者的自我修养(1):简单输入与输出

Python简单输入与输出 1.输出1.1 简单输出1.2 转义字符1.2.1 定义1.2.2 常见的转义字符用法 2.输入3.温馨提示 终于…… 终于…… 我开始玩Python了 (不是C不学了哈,C还是照更~) 今天先来简单讲下输入和输出 1.输出 1.1 简单输出 输出的函…...

java SSM 摄影作品网站myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM 摄影作品网站系统是一套完善的web设计系统(系统采用SSM框架进行设计开发,springspringMVCmybatis),对理解JSP java编程开发语言有帮助,系统具有完整的源代 码和数据库,系统主要采…...

[Maven高级]->近万字文章带你深入了解Maven

⭐作者介绍:大二本科网络工程专业在读,持续学习Java,努力输出优质文章 ⭐作者主页:逐梦苍穹 ⭐所属专栏:JavaEE ⭐如果觉得文章写的不错,欢迎点个关注一键三连😉有写的不好的地方也欢迎指正&…...

物联网Lora模块从入门到精通(五)光照与温湿度传感器

一、前言 在程序开发中,光照与温湿度的获取是十分常见与重要的,本文我们主要是使用M21温湿度光照三合一传感器,其中温湿度数据通过协议获取,而光照通过ADC获取。 二、代码实现 本文内容较为简单,且后续文章将在本文基…...

【网络编程】计算机网络基础知识总结 | 运输层 |TCP协议

文章目录 前言一、计算机网络层次结构二、网络层三、运输层3.1、TCP/IP协议介绍3.2、端口(协议端口号)3.3、套接字3.4、TCP实现原理3.4.1、TCP的特点3.4.2、停止等待协议3.4.3、滑动窗口协议3.4.4、拥塞控制3.4.5、TCP连接的三个阶段 3.5、UDP实现原理 前…...

python关键知识点

1. 变量:在程序中存储值或对象的名称。 2. 数据类型:指变量的数据类型,例如 str、int、float、list、tuple、dict、set 等。 3. 操作符:表示运算符号,例如加号 和减号 -。 4. 循环:通过重复执行某个代码…...

c# 从零到精通 数组的操作-将两个一维数组合并成一个二维数组

c# 从零到精通 数组的操作-将两个一维数组合并成一个二维数组 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Test07 { class Program { static void Main(string[] args) { //定义两个一维数组 int[] arr1 new int[] {…...

Linux目录结构(与window目录结构对比+绝对路径和相对路径)

一、Linux目录结构 Linux目录结构是一个标准化的文件系统层次结构,非常有组织性并且易于管理。而与Windows 操作系统不同,Linux将所有文件和设备都组织在一个单一的根目录下。以下是Linux的标准目录结构: /:根目录,包含…...

投票活动小程序开发搭建

由于小程序是基于微信开发者工具编写的,因此我先介绍一下需要使用的工具和技术: - 微信开发者工具:用于开发、调试和发布小程序。 - 小程序云开发:用于存储数据和进行后端逻辑处理。 - uni-app框架:uni-app 是一个使…...

代码随想录day18

513.找树左下角的值 本题用前中后序都可以(都是先遍历左再遍历右,保证最后一定是左侧的节点),因为没有中节点的处理逻辑,用全局变量记录最大深度,只要遇到叶子结点并且当前深度比最大深度大,就更…...

QT+OpenGL高级光照 Blinn-Phong和Gamma校正

QTOpenGL高级光照1 本篇完整工程见gitee:QtOpenGL 对应点的tag,由turbolove提供技术支持,您可以关注博主或者私信博主 Blinn-Phong 冯氏光照:视线与反射方向之间的夹角不小于90度,镜面光分量会变成0.0(不是很合理&am…...

【Ubuntu系统内核更新与卸载】

【Ubuntu系统内核更新与卸载】 1. 前言2. 内核安装2.1 系统更新2.2 官网下载 3. 内核卸载3.1 需求分析3.2 卸载方法 1. 前言 我们在搭建环境时常常遇到内核版本不匹配的问题,需要我们安装新的内核版本;有时又会遇到在安装软件时报错boot空间已满无法安装…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...

佰力博科技与您探讨热释电测量的几种方法

热释电的测量主要涉及热释电系数的测定,这是表征热释电材料性能的重要参数。热释电系数的测量方法主要包括静态法、动态法和积分电荷法。其中,积分电荷法最为常用,其原理是通过测量在电容器上积累的热释电电荷,从而确定热释电系数…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...

嵌入式常见 CPU 架构

架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...

如何配置一个sql server使得其它用户可以通过excel odbc获取数据

要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据,你需要完成以下配置步骤: ✅ 一、在 SQL Server 端配置(服务器设置) 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到:SQL Server 网络配…...

第八部分:阶段项目 6:构建 React 前端应用

现在,是时候将你学到的 React 基础知识付诸实践,构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段,你可以先使用模拟数据,或者如果你的后端 API(阶段项目 5)已经搭建好,可以直接连…...

CppCon 2015 学习:Time Programming Fundamentals

Civil Time 公历时间 特点: 共 6 个字段: Year(年)Month(月)Day(日)Hour(小时)Minute(分钟)Second(秒) 表示…...