用途
该技能提供一个 Compose 中“故事/短视频式自动翻页轮播”的参考实现:单个 HorizontalPager 承载全部卡片,当前页由一个统一的 LaunchedEffect 驱动计时器,到时间自动前进;界面外壳包含分段式进度条、左/右点击跳过区域和关闭按钮,卡片本身可以各自使用不同布局而不必复制翻页外壳逻辑。
主要能力与设计要点
- 单一翻页真相源:当前页码只记录在 pagerState.currentPage,不在别处维护第二份计数器,避免状态不一致。
- 按帧增量计时:计时器使用 withFrameMillis 逐帧累计 delta,而不是采样墙钟差值。暂停期间的累积不会被一次性计入,恢复时不会出现进度跳跃。
- 三重暂停门控:只有
!pressed && !busy && !pagerState.isScrollInProgress同时成立时才推进进度。pressed 对应长按阅读、busy 对应保存/分享进行中、isScrollInProgress 对应手动滑动已接管翻页。 - 空 onLongPress 的必要性:
detectTapGestures在 onLongPress 为 null 时,松手一律按普通点击处理并触发 onTap;传入非 null 回调(即使为空)后,超过长按阈值的按压会在松手前被消费,从而不触发 onTap。因此需要显式写onLongPress = { }。 - 点击动画期间读 targetPage:连续点击时若读取 currentPage,会重复滚动到正在离开的页面;读取 targetPage 才能从第一次点击的目标继续前进或后退。
- 分段进度条数量取过滤后的列表:卡片种类可能因数据不足被过滤掉(例如没有最大单日或日期数据太少),页面数、关闭/前进判断、分段行都应使用过滤后列表的 size,而非枚举全量。
- 首帧前一次性解析数据:整年数据在一个函数中计算为不可变结果,由三态持有者(loading / not-enough-data / ready)控制,pager 只在 ready 状态存在,避免翻页中途出现加载 spinner。
- 导出卡片固定主题输入:需要作为分享图片导出的卡片,配色应固定
isDark = true并基于年份自身素材构建,不读取设备主题,确保同一卡片在不同设备上导出的图片一致。 - 捕获区域限定在卡片内海报:带 Save/Share 按钮的卡片只将捕获修饰符传给内层面板,按钮与上方 eyebrow 不属于导出图;同时先应用底部 chrome padding 再背景与捕获,避免 padding 被烘进导出图边缘。
使用方式
该技能以 Kotlin/Compose 源码形式提供实现与验证命令。验证时从包含 ui/ 与 viewModel/ 包的 Kotlin 源根目录运行 grep 检查,例如确认三重门控条件、空 onLongPress = { }、pagerState.targetPage 与 pagerState.currentPage 的分别使用、cards.size 与 WrappedCard.entries.filter 的计数来源。资料中给出的命令针对示例路径 ui/screen/home/wrapped/WrappedScreen.kt 与 viewModel/WrappedUiState.kt,实际使用时需调整为自己的路径。
适用场景
适用于在 Android Jetpack Compose 应用中实现类似 Instagram Stories 的自动播放卡片流,尤其是每张卡片内容不同、需要统一外壳交互、需要长按暂停阅读、需要导出分享图片的场景。
限制
该技能为 Compose 实现,依赖 androidx.compose.foundation 的 TapGestureDetector 行为;资料未提供除上述 grep 验证外的自动化测试或完整示例工程。具体版本兼容性、性能表现及非 Compose 平台的适配暂未提供。