☰
D16_HorizontalPager页签与状态
2026/10/14 8:24:20 网站建设 项目流程

Android 学习笔记 D16|HorizontalPager 的页码、滚动位置与页面身份

发布摘要:把文章和收藏放进同级滑页,解决页签同步、列表状态归属、重复请求和详情返回后的恢复。标签:Compose、HorizontalPager、导航、Android。

DevCommunity 有文章和收藏两个入口,左右滑动比反复进入退出页面更直接。不过滑页加上去以后,常见问题也一起出现:滑到了收藏,顶部仍选中文章;进入详情回来,列表回到顶部;来回切十次,接口也请求十次。本课不是只完成一个能拖动的容器,而是明确每一种状态由谁管理。

本文对应 D16。书中第 4 章的列表和第 5 章的页面组织提供基础,Compose Foundation Pager 属于路线扩展。下面代码是未编译运行的核心片段,使用项目现有 Compose BOM 对应的 Foundation、Material3 和协程依赖。

一、同级切换与进入详情是两种动作

文章和收藏是首页内部的同级内容,Pager 管当前页和拖动过程。文章详情是新的阅读目的地,仍由路线中的 Navigation 3 返回栈管理。不要把详情也塞成第三页,否则用户从收藏点一篇文章之后,向左滑动到底应回文章还是收藏,就会变得含糊。

首页拥有三个短期状态:当前页、文章列表滚动位置、收藏列表滚动位置。Repository 拥有收藏关系;页面不各自维护互相竞争的收藏布尔值。进入详情时传文章编号,返回到原首页条目后,恢复它原来的页码和滚动位置。

currentPage跟随当前吸附位置,适合页签提示;settledPage表示滚动结束后稳定的页面,targetPage表示滚动预计到达的位置。选择哪个取决于业务语义。Pager 状态说明

二、建立唯一的页码来源

下面用普通按钮做页签,先验证行为再替换正式样式。导入来自androidx.compose.foundation.pager、foundation.lazy、foundation.layout、material3、runtime、ui.Modifier和kotlinx.coroutines.launch。两个参数由调用方提供,接到状态后传给各自的 LazyColumn。

@ComposablefunArticleTabs(feed:@Composable(LazyListState)->Unit,saved:@Composable(LazyListState)->Unit){valpager=rememberPagerState(pageCount={2})valfeedList=rememberLazyListState()valsavedList=rememberLazyListState()valscope=rememberCoroutineScope()Column(Modifier.fillMaxSize()){Row{listOf("文章","收藏").forEachIndexed{index,title->TextButton(onClick={scope.launch{pager.animateScrollToPage(index)}}){Text(if(pager.currentPage==index)"●$title"elsetitle)}}}HorizontalPager(state=pager,modifier=Modifier.weight(1f)){page->when(page){0->feed(feedList)1->saved(savedList)}}}}

这里不再另外维护selectedTab。按钮让 Pager 滚动,Pager 当前页反过来决定选中显示,手势和点击因此共用同一份事实。如果另设一个整数,只在按钮回调中改变它,手势就不会自动同步,这不是动画问题,而是重复状态问题。

第一步先提供静态两页;第二步接入原来的文章和收藏界面;第三步把两个列表状态提升到 Pager 外;第四步进入详情并返回,检查首页条目的保存状态配置。局部 remember 能否随导航离开后恢复,还取决于目的地保存状态机制,不能只凭这段代码保证完整流程。Navigation 3 状态保存

三、为什么组合不等于访问

Pager 会按需要创建页面,也可能提前组合附近页面。因此把网络请求写在页面函数体里,相当于把框架执行细节当成用户动作。一个状态变化引起的重组,就可能触发第二次请求;邻页预备出现,也可能被误报成一次浏览。

文章加载应由首页状态持有者发起,并明确首次加载、用户刷新、缓存过期三种入口。收藏页观察数据库流,通常无需每次切入再拉一次远端列表。加载是否重复可以用请求编号记录,不能只看加载圈,因为快速请求可能一闪而过。

如果业务要求统计阅读某页,应在观察稳定页码之外,再结合宿主前台状态、页面是否实际展示、最低停留时间和去重规则。当天可以只记录页码变化作调试,不要把这个调试日志直接宣称为准确曝光统计。页面状态设计越清楚,后续加入搜索页越不容易改乱。

四、三组故障实验

第一组故意加回独立的页签整数,只在点击时更新,然后手势滑到另一页。预期能复现选中提示错位。删掉重复状态后再测点击、慢拖和快速拖动,三者都应由同一页码驱动。

第二组在两页内各放一份收藏集合。文章页收藏后切到收藏页,预期暴露数据不一致。修复时将收藏关系收敛到 Repository 和数据库;不要仅增加“切页后强制刷新”,因为详情页也可能修改相同数据。

第三组把收藏列表滚到中间,进入某篇详情再返回。分别记录返回栈中的首页身份、Pager 页码和列表首项编号。预期恢复到原来的收藏位置;若页码正确而列表位置不对,应先查 LazyListState 的归属和稳定 key,而不是重写整个导航。

这些都是受控实验与预期,尚未代表用户工程完成实测。博客配图可以使用三列对照:操作、修改前现象、修改后证据,避免仅展示一张最终界面图。

五、原创面试问答与追问

本篇 Pager 问题为课程自拟,可联系题库的传统 UI 与 Compose UI继续复习。

问:为什么不让每个页签维护收藏状态?答:两页展示的是同一份业务关系,副本很容易出现写入和通知不同步。追问:页内可以有自己的状态吗?可以,滚动位置和临时输入属于各页的界面状态。

问:currentPage 变化能说明页面停稳了吗?答:不能,它可能在手势进行中改变。追问:用 settledPage 就能保证用户看过了吗?也不能,还需要考虑可见性和业务停留条件。

问:返回后位置丢失先查哪里?答:先区分目的地是否被重新创建、保存状态是否接入、列表状态是否属于正确页面。追问:给所有列表同一个 key 可以吗?不可以,不同列表需要各自清楚的身份。

六、当天练习与验收

当天二十分钟回忆导航与状态,四十分钟阅读 Pager API,一百五十分钟实现与诊断,三十分钟做回归,一小时闭卷讲解。必做交付是两页滑动演示、十次切页请求计数、详情返回记录。

独立练习是在不添加第二份页码的条件下增加“回到文章”按钮,并使收藏页的滚动位置仍然保留。验收时要说出一次滑动改变了哪个状态、没有改变哪些长期数据,以及为何页面重组不应自动产生网络请求。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询