Skip to content

Week View

lingion edited this page Sep 12, 2026 · 1 revision

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,属防御性保留。

选周菜单只在「实际周」点胶囊才弹出;翻到别的周再点胶囊是一键跳回实际周,细节见 课表页总览。

行分组引擎:weekLaneRows

DetailPanel 每天的课程卡不逐门罗列,先过一遍 ConflictLayoutEngine.weekLaneRows 分成渲染行(CourseTableView.kt:963-965;util/ConflictLayoutEngine.kt:489-516)。算法四步:

  1. 自定义时间的课先用作息表归一化等效节次(ConflictLayoutEngine.kt:491)。
  2. 按天分桶、按开始节次排序,线性扫出连通冲突区域:相邻课程区间相交就并入当前区域,传递闭包。1-2 节和 2-3 节相连,2-3 节又和 3-4 节相连,三门课整段算一个区域,虽然头尾两门互不重叠(ConflictLayoutEngine.kt:390-405)。
  3. 带 timeJson 时重叠判定走分钟域:常规课取节次真实起止,自定义时间课取自填起止,分钟区间相交才算冲突;解析不出时间的脏数据宁可判不相交,不制造假冲突(ConflictLayoutEngine.kt:94-139)。
  4. 区域内跑 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(灰显判定)。

Clone this wiki locally