fix: 年视图 pageYear 使用 settledPage 计算,修复滑动切换时年份闪烁

pageYear = yearViewYear + (page - settledPage) 代替 currentPage,
因为 yearViewYear 由 settledPage 驱动,两者始终同步。
用 currentPage 会在滑动过半时出现不匹配,导致小月份
短暂显示错误年份数据。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
xfy 2026-05-22 16:09:42 +08:00
parent 2fc89d08c7
commit dd072730f1
13 changed files with 336 additions and 5 deletions

View File

@ -24,7 +24,10 @@ YaYa 是一款基于纯 Android + Jetpack Compose 构建的日历应用。应用
|-----------|---------| |-----------|---------|
| `app/` | Android 应用壳层模块(见 `app/AGENTS.md` | | `app/` | Android 应用壳层模块(见 `app/AGENTS.md` |
| `core/` | Android Library 核心模块:所有 Compose UI、ViewModel 和业务逻辑(见 `core/AGENTS.md` | | `core/` | Android Library 核心模块:所有 Compose UI、ViewModel 和业务逻辑(见 `core/AGENTS.md` |
| `gradle/` | Gradle Wrapper 文件 | | `macrobenchmark/` | 宏基准测试模块Baseline Profile 生成(见 `macrobenchmark/AGENTS.md` |
| `gradle/` | Gradle Wrapper 文件(见 `gradle/wrapper/AGENTS.md` |
| `scripts/` | 性能追踪脚本(见 `scripts/AGENTS.md` |
| `logs/` | 性能追踪日志输出目录(见 `logs/AGENTS.md` |
## For AI Agents ## For AI Agents

33
app/AGENTS-anim.md Normal file
View File

@ -0,0 +1,33 @@
<!-- Parent: ../../AGENTS.md -->
<!-- Generated: 2026-05-22 | Updated: 2026-05-22 -->
# anim
## Purpose
Activity 转场动画资源目录。定义 Activity 进入和退出的滑入/滑出动画效果,配合 `overridePendingTransition()` 使用。
## Key Files
| File | Description |
|------|-------------|
| `slide_in_right.xml` | 从右侧滑入duration: 350msfast_out_slow_in 插值器) |
| `slide_in_left.xml` | 从左侧滑入 |
| `slide_out_left.xml` | 向左侧滑出 |
| `slide_out_right.xml` | 向右侧滑出 |
## Subdirectories
## For AI Agents
### Working In This Directory
- 新增转场动画需同步更新对应 Activity 的 `overridePendingTransition()` 调用
- 动画时长建议 300-400ms使用 `fast_out_slow_in` 插值器保持 Material Design 一致性
## Dependencies
### Internal
- `app/src/main/kotlin/plus/rua/project/AboutActivity.kt` — 使用 slide_in_right / slide_out_left
- `app/src/main/kotlin/plus/rua/project/LicensesActivity.kt` — 使用 slide_in_right / slide_out_left
<!-- MANUAL: -->

31
app/AGENTS-values.md Normal file
View File

@ -0,0 +1,31 @@
<!-- Parent: ../../AGENTS.md -->
<!-- Generated: 2026-05-22 | Updated: 2026-05-22 -->
# values
## Purpose
Android 值资源目录,包含应用字符串和 Material 3 主题定义。
## Key Files
| File | Description |
|------|-------------|
| `strings.xml` | 应用名称字符串(`app_name: YaYa` |
| `themes.xml` | 日间主题:`Theme.Material.Light.NoActionBar` |
## Subdirectories
## For AI Agents
### Working In This Directory
- 新增字符串资源在此文件中定义
- 主题继承自 `Theme.Material.Light.NoActionBar`,支持 `enableEdgeToEdge()` 全屏显示
- 夜间模式主题在 `../values-night/themes.xml` 中定义
## Dependencies
### Internal
- `app/src/main/AndroidManifest.xml` — 引用主题
<!-- MANUAL: -->

View File

@ -27,7 +27,9 @@ Android 应用主 source set包含入口 Activities、应用清单、主题
|-----------|---------| |-----------|---------|
| `kotlin/plus/rua/project/` | Kotlin 源码(见 `kotlin/plus/rua/project/AGENTS.md` | | `kotlin/plus/rua/project/` | Kotlin 源码(见 `kotlin/plus/rua/project/AGENTS.md` |
| `res/` | Android 资源文件(图标、主题、字符串、动画) | | `res/` | Android 资源文件(图标、主题、字符串、动画) |
| `assets/` | 原始资产文件GIF 等) | | `res/anim/` | Activity 转场动画 XML`res/anim/AGENTS.md` |
| `res/values/` | 字符串和主题定义(见 `res/values/AGENTS.md` |
| `assets/` | 原始资产文件GIF 等)(见 `assets/AGENTS.md` |
## For AI Agents ## For AI Agents

View File

@ -0,0 +1,25 @@
<!-- Parent: ../AGENTS.md -->
<!-- Generated: 2026-05-22 | Updated: 2026-05-22 -->
# assets
## Purpose
原始资产文件目录,存放不需要 Android 资源系统处理的二进制文件。当前包含 GIF 动画资源。
## Key Files
无顶层文件
## Subdirectories
| Directory | Purpose |
|-----------|---------|
| `gifs/` | GIF 动画资源(见 `gifs/AGENTS.md` |
## For AI Agents
### Working In This Directory
- 新增原始资源文件直接放入此目录或其子目录
- 通过 `AssetManager` 在运行时访问
<!-- MANUAL: -->

View File

@ -0,0 +1,33 @@
<!-- Parent: ../AGENTS.md -->
<!-- Generated: 2026-05-22 | Updated: 2026-05-22 -->
# gifs
## Purpose
GIF 动画资源目录,存放应用使用的动画 GIF 文件001.gif ~ 152.gif`AnimatedGif` Composable 组件通过 `sketch` 库加载显示。
## Key Files
| File | Description |
|------|-------------|
| `001.gif` ~ `152.gif` | 应用动画 GIF 资源 |
## Subdirectories
## For AI Agents
### Working In This Directory
- 替换或新增 GIF 时保持连续编号
- GIF 文件较大,注意 APK 体积影响
- `AnimatedGif` 组件使用 `sketch` 库异步加载和播放
## Dependencies
### Internal
- `core/src/main/kotlin/plus/rua/project/ui/AnimatedGif.kt` — GIF 显示组件
### External
- `sketch` 4.4.0GIF 解码和播放)
<!-- MANUAL: -->

View File

@ -0,0 +1,25 @@
<!-- Parent: ../AGENTS.md -->
<!-- Generated: 2026-05-22 | Updated: 2026-05-22 -->
# assets
## Purpose
核心模块原始资产文件目录,与 `app/src/main/assets/` 镜像,包含 GIF 动画资源供 `AnimatedGif` Composable 组件加载。
## Key Files
无顶层文件
## Subdirectories
| Directory | Purpose |
|-----------|---------|
| `gifs/` | GIF 动画资源(见 `gifs/AGENTS.md` |
## For AI Agents
### Working In This Directory
- 此目录与 `app/src/main/assets/` 内容同步
- 通过 `AssetManager` 在运行时访问
<!-- MANUAL: -->

View File

@ -0,0 +1,33 @@
<!-- Parent: ../AGENTS.md -->
<!-- Generated: 2026-05-22 | Updated: 2026-05-22 -->
# gifs
## Purpose
GIF 动画资源目录,存放应用使用的动画 GIF 文件001.gif ~ 152.gif`AnimatedGif` Composable 组件通过 `sketch` 库加载显示。
## Key Files
| File | Description |
|------|-------------|
| `001.gif` ~ `152.gif` | 应用动画 GIF 资源 |
## Subdirectories
## For AI Agents
### Working In This Directory
- 替换或新增 GIF 时保持连续编号
- GIF 文件较大,注意 APK 体积影响
- `AnimatedGif` 组件使用 `sketch` 库异步加载和播放
## Dependencies
### Internal
- `core/src/main/kotlin/plus/rua/project/ui/AnimatedGif.kt` — GIF 显示组件
### External
- `sketch` 4.4.0GIF 解码和播放)
<!-- MANUAL: -->

View File

@ -143,8 +143,7 @@ fun CalendarMonthView(
snapshotFlow { yearPagerState.settledPage }.collect { page -> snapshotFlow { yearPagerState.settledPage }.collect { page ->
if (page != lastSettledPage) { if (page != lastSettledPage) {
val diff = page - lastSettledPage val diff = page - lastSettledPage
val newYear = viewModel.yearViewYear.value + diff viewModel.setYearViewYear(viewModel.yearViewYear.value + diff)
viewModel.setYearViewYear(newYear)
lastSettledPage = page lastSettledPage = page
} }
} }
@ -289,7 +288,7 @@ fun CalendarMonthView(
} else { } else {
pageOffset pageOffset
} }
val pageYear = yearViewYear + (page - yearPagerState.currentPage) val pageYear = yearViewYear + (page - yearPagerState.settledPage)
YearGridView( YearGridView(
year = pageYear, year = pageYear,
selectedMonth = if (pageYear == currentYear) currentMonth else 0, selectedMonth = if (pageYear == currentYear) currentMonth else 0,

31
gradle/wrapper/AGENTS.md vendored Normal file
View File

@ -0,0 +1,31 @@
<!-- Parent: ../../AGENTS.md -->
<!-- Generated: 2026-05-22 | Updated: 2026-05-22 -->
# wrapper
## Purpose
Gradle Wrapper 目录,包含 `gradle-wrapper.jar``gradle-wrapper.properties`。Wrapper 允许项目在没有预装 Gradle 的环境中使用固定版本的 Gradle 构建。
## Key Files
| File | Description |
|------|-------------|
| `gradle-wrapper.jar` | Gradle Wrapper 可执行 JAR |
| `gradle-wrapper.properties` | Wrapper 配置Gradle 发行版 URL当前 9.5.1 |
## Subdirectories
## For AI Agents
### Working In This Directory
- 通常不需要手动修改此目录内容
- 升级 Gradle 版本时使用 `./gradlew wrapper --gradle-version=X.Y.Z`
- `gradle-wrapper.properties` 中的 `distributionUrl` 指向 Gradle 官方发行版
## Dependencies
### External
- Gradle 9.5.1
<!-- MANUAL: -->

44
macrobenchmark/AGENTS.md Normal file
View File

@ -0,0 +1,44 @@
<!-- Parent: ../AGENTS.md -->
<!-- Generated: 2026-05-22 | Updated: 2026-05-22 -->
# macrobenchmark
## Purpose
宏基准测试模块,使用 Android Baseline Profile 技术自动生成启动优化配置文件。通过 UI Automator 模拟真实用户交互路径,收集 AOT 编译所需的 profile 数据。
## Key Files
| File | Description |
|------|-------------|
| `build.gradle.kts` | 模块构建配置(`com.android.test` 插件、benchmark 构建类型、Baseline Profile 自动复制任务) |
## Subdirectories
| Directory | Purpose |
|-----------|---------|
| `src/main/java/plus/rua/project/baseline/` | Baseline Profile 生成器(见 `src/main/java/plus/rua/project/baseline/AGENTS.md` |
## For AI Agents
### Working In This Directory
- 修改此模块需连接 Android 设备/模拟器运行
- Baseline Profile 一键生成:`./gradlew :macrobenchmark:updateBaselineProfile`
- 仅运行基准测试:`./gradlew :macrobenchmark:connectedBenchmarkAndroidTest`
- 生成的 profile 自动复制到 `core/src/main/baseline-prof.txt`
### Testing Requirements
- 需要已安装应用的 benchmark 构建类型
- 模拟器需启用 GPU 加速software renderer 不支持 gfxinfo framestats
## Dependencies
### Internal
- `:app` 模块 — 目标测试应用
- `:core` 模块 — profile 自动复制到 `core/src/main/baseline-prof.txt`
### External
- `androidx.benchmark:benchmark-macro-junit4`
- `androidx.test.uiautomator`
- `androidx.test.ext:junit`
<!-- MANUAL: -->

View File

@ -0,0 +1,35 @@
<!-- Parent: ../../../../../../../AGENTS.md -->
<!-- Generated: 2026-05-22 | Updated: 2026-05-22 -->
# baseline
## Purpose
Baseline Profile 自动生成器。通过 UI Automator 模拟完整用户交互路径冷启动、FAB 展开、年/月视图切换、日期选择、折叠/展开、关于页、开源许可页),生成 AOT 编译优化所需的 startup profile。
## Key Files
| File | Description |
|------|-------------|
| `BaselineProfileGenerator.kt` | Profile 生成测试类,覆盖全部用户交互路径 |
## Subdirectories
## For AI Agents
### Working In This Directory
- 新增交互路径需在 `BaselineProfileGenerator.kt``profileBlock` 中添加对应步骤
- 使用 `device.findObject(By.res(...))``device.findObject(By.text(...))` 定位 UI 元素
- 每个操作后调用 `device.waitForIdle()` 等待动画完成
### Testing Requirements
- 运行:`./gradlew :macrobenchmark:updateBaselineProfile`
- 需要设备/模拟器连接,应用已安装在 benchmark 构建类型下
## Dependencies
### External
- `androidx.benchmark.macro.junit4.BaselineProfileRule`
- `androidx.test.uiautomator`
<!-- MANUAL: -->

37
scripts/AGENTS.md Normal file
View File

@ -0,0 +1,37 @@
<!-- Parent: ../AGENTS.md -->
<!-- Generated: 2026-05-22 | Updated: 2026-05-22 -->
# scripts
## Purpose
开发辅助脚本目录,包含性能追踪工具。使用 Perfetto 在设备上抓取应用 trace并生成结构化报告。
## Key Files
| File | Description |
|------|-------------|
| `profile.sh` | Perfetto 性能追踪脚本:抓取 trace、帧统计、内存快照生成 Markdown 报告 |
## Subdirectories
## For AI Agents
### Working In This Directory
- 修改脚本后确保 `adb` 可用性检查和设备连接检测逻辑保持完整
- 脚本输出到 `logs/` 目录trace、framestats、meminfo、report
### Testing Requirements
- 需要已安装 `adb` 和连接的设备
- 应用需已安装:`./gradlew :app:installDebug`
## Dependencies
### Internal
- `logs/` — 脚本输出目录
### External
- Android SDK `adb`
- 设备上的 `perfetto` 工具
<!-- MANUAL: -->