-
Notifications
You must be signed in to change notification settings - Fork 9
Week View
一句话 TL;DR:这页讲周视图的页面结构、翻周三向同步、冲突行分组算法、两栏分法、灰显逻辑和每个设置项的默认值。
本文基于 v1.0.54(versionCode 58)。
周视图是课表页两种视图之一,顶部切换器里标作「周视图」(app/src/main/res/values-zh-rCN/strings.xml:403;切换器代码 app/src/main/java/com/lingion/sleepy/ui/screen/schedule/ScheduleScreen.kt:197-204)。顶栏、切换器、翻周三件套的交互归 课表页总览,这页只拆周视图本体。
视图装在一个 HorizontalPager 里,一页一周,页数等于课表的总周数 maxWeek,首页落在启动时的 selectedWeek(ScheduleScreen.kt:207-211)。每页先按 inWeek(page + 1) 过滤出本周课程,再用作息表把自定义时间的课归一化出等效节次(ScheduleScreen.kt:236-240),然后交给 FullWeekView(ui/component/CourseTableView.kt:593)。
页内两块,共用一根纵向滚动(CourseTableView.kt:624-656):
┌──────────────────────────────────────────────┐
│ TopBar(‹ 第 3 周 ›)← 全页共享,不属周视图 │
├──────────────────────────────────────────────┤
│ WeekStrip 周条 │
│ ┌────┬────┬────┬────┬────┬────┬────┐ │
│ │周一│周二│周三│周四│周五│周六│周日│ │
│ │3 门│2 门│ … │ │ │ │ │ │
│ │高数│英语│ │ │ │ │ │ │
│ └────┴────┴────┴────┴────┴────┴────┘ │
│ DetailPanel 详情面板(纵向滚动主体) │
│ ┌────────────────────────────────────────┐ │
│ │ 周一 · 今天 │ │
│ │ [3-4节│高等数学A│张三 · A101] │ │
│ │ [8-9节│影视鉴赏] │ │
│ └────────────────────────────────────────┘ │
└──────────────────────────────────────────────┘
←→ 手势左右滑 = 上一周 / 下一周
WeekStrip 每个 DaySummaryCell 占一份 weight,间距 6dp 乘缩放(CourseTableView.kt:671-689);格高 132dp 乘缩放,圆角 12dp 乘缩放再乘圆角比例(CourseTableView.kt:713-716)。今天那格换 primaryContainer 底色(CourseTableView.kt:706)。
摘要格从上到下三段:本地化星期名;课程数胶囊「3 门」(列宽装不下完整文字时自动退化为纯数字「3」;无课不显示胶囊,CourseTableView.kt:729-759);前 5 门课名,每门最多两行省略(CourseTableView.kt:764-777)。课名跟随周视图别名开关。
DetailPanel 每天一张 DetailDayCard:头部是星期名加「 · 今天」后缀,无课时整卡换 surfaceContainerLow 底色并写「周X · 无课程」(CourseTableView.kt:935-956)。
翻周有三条入口:手势滑动、顶栏 ‹ › 箭头、「第 N 周」胶囊的下拉选周菜单。三条全部汇到 viewModel.changeWeek,值先钳制在 1..maxWeek,越界直接忽略(ui/screen/schedule/ScheduleViewModel.kt:243-247)。
Pager 与 ViewModel 之间两根 LaunchedEffect 接成双向(ScheduleScreen.kt:213-229):
- 手势侧:监听
pagerState.currentPage,翻完页把currentPage + 1写回 ViewModel。 - ViewModel 侧:监听
selectedWeek,与当前页不同就scrollToPage(targetPage)。
防打架靠 scrollToPage 本身:它瞬时落位、没有动画。箭头或菜单改了 selectedWeek,Pager 立刻跳到位,回声触发的手势侧回调写回的是同一个周数,StateFlow 按值去重不再发射,环路到此断掉。手势路径独占翻页动画。代码里另有一个 syncingFromState 布尔闸标着手势侧入口(ScheduleScreen.kt:214,218),当前恒为 false,属防御性保留。
选周菜单只在「实际周」点胶囊才弹出;翻到别的周再点胶囊是一键跳回实际周,细节见 课表页总览。
DetailPanel 每天的课程卡不逐门罗列,先过一遍 ConflictLayoutEngine.weekLaneRows 分成渲染行(CourseTableView.kt:963-965;util/ConflictLayoutEngine.kt:489-516)。算法四步:
- 自定义时间的课先用作息表归一化等效节次(
ConflictLayoutEngine.kt:491)。 - 按天分桶、按开始节次排序,线性扫出连通冲突区域:相邻课程区间相交就并入当前区域,传递闭包。1-2 节和 2-3 节相连,2-3 节又和 3-4 节相连,三门课整段算一个区域,虽然头尾两门互不重叠(
ConflictLayoutEngine.kt:390-405)。 - 带 timeJson 时重叠判定走分钟域:常规课取节次真实起止,自定义时间课取自填起止,分钟区间相交才算冲突;解析不出时间的脏数据宁可判不相交,不制造假冲突(
ConflictLayoutEngine.kt:94-139)。 - 区域内跑
chainGroups分栏(lane):反复用区间图贪心(按右端点)挑出互不重叠的最大课程集合成一栏,剩下的继续分(ConflictLayoutEngine.kt:351-378)。栏数等于分组数,重叠课异栏,不重叠课同栏。
产出是行列表:无冲突课一行一门全宽;冲突区域整区域一行,横向 laneCount 栏,同栏课纵向堆叠,堆叠序天然就是时间序。行序按行首课的节次排(ConflictLayoutEngine.kt:515)。区域是划分,每门课恰属一行,结构上不可能丢课或重复。
两门冲突课的排法:
周一(高等数学A 3-4节 与 大学英语 4-5节 重叠):
┌──────────────────────┬─┬──────────────────────┐
│ 3-4节 高等数学A │ │ 4-5节 大学英语 │
│ 张三 · A101 │d│ 李四 · C301 │
└──────────────────────┴─┴──────────────────────┘
┌────────────────────────────────────────────┐
│ 8-9节 影视鉴赏(无冲突,独占整行) │
└────────────────────────────────────────────┘
中间的 d 是栏间分隔线:0.5dp 宽,onSurface 色 30% 透明,高度随行撑满(CourseTableView.kt:993-1003)。栏间还有 6dp 间距,每栏宽 = (行宽 − 间距×(栏数−1)) / 栏数(CourseTableView.kt:984-986)。
栏一窄,内容按实宽压缩:栏宽 ≥150dp 保持原样,以下线性缩字号,0.6 封底(weekLaneFontScale,CourseTableView.kt:1036-1047);栏宽不足 110dp 时隐藏左侧节次/时间标签和教师教室副信息,把位置让给课名(CourseTableView.kt:1049-1050)。
这个引擎是三处共用的:App 周视图、今日页、全部桌面组件走同一个 weekLaneRows,一份真相(CourseTableView.kt:964;widget/WidgetBitmapRenderers.kt:431)。网格视图的冲突叠层是另一套(layoutCluster),见 冲突课程。
默认单栏,七天竖排一个面板。开「周视图两栏显示」后拆左右两个 DayColumn,各占一半宽、各自独立面板底(CourseTableView.kt:808-845)。分法二选一(CourseTableView.kt:809-821):
- 按天对半分(默认):天数固定对半,前半周左、后半周右。7 天 = 4+3,6 天 = 3+3,奇数天多出的那一天落左栏。
- 按课程数平衡:逐天贪心,每天记权重 = max(课程数, 1)(空天也有卡头所以记 1),放进当前累计权重更矮的那栏,两栏高度接近。天的位置不固定。例:周一 3 门、周二 2 门、周三 4 门、周四 1 门、周五 0 门 → 左 = 周一/周四/周五(权重 3+1+1),右 = 周二/周三(权重 2+4)。
单栏: 两栏·按天对半分(7 天→4+3):
┌────────────┐ ┌────────┬────────┐
│ 周一 │ │ 周一 │ 周五 │
│ 周二 │ → │ 周二 │ 周六 │
│ … │ │ 周三 │ 周日 │
│ 周日 │ │ 周四 │ │
└────────────┘ └────────┴────────┘
「隐藏无课日」与两栏的交互:过滤发生在分栏之前,7 天滤掉无课的周日剩 6 天,照样 3+3(CourseTableView.kt:799-802)。过滤后不足 2 天(含全周无课)则回落单栏,并显示全部所选星期,页面不空(CourseTableView.kt:846-856)。注意隐藏无课日只作用于两栏布局的详情面板:单栏分支遍历的是未过滤的可见星期(CourseTableView.kt:856),周条 WeekStrip 永远七个格子全渲染(CourseTableView.kt:675)。
每页用 produceState 算出本周该灰显的星期集合:逐天算出实际日期,交给 HolidayManager.shouldGrey 判定(ScheduleScreen.kt:242-254)。判定规则(util/HolidayManager.kt:202-219):
- 日期落在法定节假日 → 灰,受「法定节假日灰显」开关控制(默认开)。
- 周六周日 → 灰,受「周末灰显」开关控制(默认开);但补班日(周末但要上课的日子)在「忽略补班日」开关开启时豁免(默认开)。
- 三者全关则永不灰。
灰显视觉落在课程卡和摘要格两处:课色块与文字降到 60% 透明(Alpha.inactive,CourseTableView.kt:1084-1085);「灰显样式」选「删除线」时文字加 LineThrough(默认「变灰」不加线,CourseTableView.kt:1086-1087)。节假日数据来源、用户自定义覆盖与三个开关的完整语义见 节假日系统。
设置入口在 我的 → 通用设置,改动通过 AppPrefs.changeBus 即时驱动周视图重组(CourseTableView.kt:605-613)。滑杆步进 0.05(ui/screen/mine/GeneralSettingsScreen.kt:233-234)。
| 设置项 | 键 | 默认 | 说明 |
|---|---|---|---|
| 周视图缩放 | week_scale |
1.0 | 范围 0.7~1.3。字号/行高/间距/圆角/内边距等比联动,与网格视图缩放互相独立(util/AppPrefs.kt:459-465) |
| 卡片圆角 | grid_corner_ratio |
1.0 | 范围 0~2,乘基准圆角(课程卡 12dp、面板外框 16dp)。网格与周视图共用一个值(AppPrefs.kt:467-476) |
| 周视图两栏显示 | week_two_column |
关 | 拆左右两栏省纵向滚动(AppPrefs.kt:483-489) |
| 分栏方式 | week_two_column_mode |
days |
「按天对半分」或「按课程数平衡」,两栏开启时才可选(AppPrefs.kt:491-497) |
| 周视图隐藏无课日 | week_hide_empty_days |
关 | 隐藏当天无课的星期,仅两栏布局下生效(AppPrefs.kt:499-505) |
| 周视图显示别名 | week_use_alias |
关(原名) | 开启后展示课程别名;别名为空串回退原名。别名只改展示,详情/导出/通知仍用原名(AppPrefs.kt:511-517;util/CourseDisplayUtil.kt:18-22) |
| 课程时间显示 | display_mode |
node |
全局开关,「节次」或「时间」,决定课程卡左侧标签形态,见下节(AppPrefs.kt:226-231) |
一张 LessonRow 从左到右:左侧标签 + 右侧内容区(CourseTableView.kt:1095-1168)。
- 课名:原名或别名,最多两行省略;两栏窄栏下再乘栏宽压缩系数。
-
左侧标签(固定宽 42dp 乘缩放):全局「课程时间显示」为「节次」时显示「3-4节」这样的短节次串;切到「时间」时显示「08:00-\n08:45」,起止时间在连字符后折行,行距收紧读成一个整体(
CourseTableView.kt:1090-1091,1112-1127)。时间模式需要课表带作息表,解析不出就回落节次标签;自定义时间的课无论开关状态都直接显示自填的起止时间(data/entity/CourseEntity.kt:148-157处shortNodeString;util/TimeTableUtils.kt:91-101)。 -
副信息:教师和教室用「 · 」拼接(「张三 · A101」),只填其一就只显示那项,两项都空则整行不占位;栏宽不足 110dp 时隐藏(
CourseTableView.kt:1146-1165)。 - 底色:课程色,文字色按底色明暗自适应;灰显天降透明加可选删除线。
-
点击:弹课程详情底部弹窗(
ScheduleScreen.kt:261)。
「课程时间显示」同时作用于周视图与全部桌面组件;网格视图不消费这个开关,网格卡片由纵向位置本身表达节次。周条摘要格也不受它影响,只显示课名(widget/WidgetBitmapRenderers.kt:323 是桌面组件侧的读取点;网格卡不读 display_mode)。
源码:
ui/screen/schedule/ScheduleScreen.kt(HorizontalPager 与翻周同步)、ui/component/CourseTableView.kt(FullWeekView / WeekStrip / DetailPanel / LessonRow)、util/ConflictLayoutEngine.kt(weekLaneRows / chainGroups)、util/AppPrefs.kt(设置键与默认值)、util/HolidayManager.kt(灰显判定)。
Sleepy Wiki
入门
界面与视图
课程管理
导入导出
小组件与提醒
数据与设置
项目
社区