List 列表与 LazyForEach工程化笔记:可读代码与可复现页面并行推进
List、ForEach 与“懒加载”展示页:从 ArkTS 页面读懂状态、条目与实现边界
这是一个很小的 ArkTS 单页应用。它把消息、联系人和“数据量”这三种经常出现在列表页面里的话题放在了同一张页面上:顶部有三个页签,第一页展示五条消息,第二页展示五位联系人,第三页有一个“追加 1000”按钮和四条示例文字。打开应用后即可看到这套连续的页面内容。
页面标题里同时出现了 List 和 LazyForEach,很容易让人把它当成已经完成的长列表示例。实际可见行为是:当前页面没有使用 List 或 LazyForEach,重复内容由 ForEach 生成;第三个页签也没有一万条真实数据,按钮只会把一个计数状态增加一千,下方始终只有四条固定文字。它更适合用来观察列表页面的结构与状态,并把“按需构建的大列表”与当前体验明确分开。

初始画面中五条消息卡片的排列还体现了页面对信息密度的控制。消息标题使用相对醒目的深色,具体内容采用较小的灰色文字,左侧圆形编号形成连续的视觉节奏,右侧状态词则让用户知道每一行都可以被进一步查看。五条内容之间留出稳定的空隙,卡片没有额外的头像、时间和操作菜单,因此用户可以把注意力集中在「哪一条被选中」这一个变化上。页面没有将消息做成横向滑动卡片,也没有用弹窗承载详情,首次打开后从上到下就能看完整个基础列表。
当用户先切换到联系人,再返回基础列表时,消息区依然从第一页开始显示,页面没有把联系人文字混入消息卡片。随后点击某一条消息,变化只发生在被点击的卡片内部:编号圆形换成紫色,右侧文字换成“已读”,标题和摘要不变。这个对照可以帮助读者把「内容本身」和「当前选中状态」分开理解。内容是固定的,状态是临时的;状态变化不会改写消息文字,也不会把消息移到其他位置。
先运行,再确认自己看到的是什么
打开应用即可进入这张页面。启动后屏幕上看到的是标题、三个页签和当前页签内容,没有隐藏的第二页面,也没有需要先填写的路由参数。
初始状态下,标题为“列表与懒加载”,副标题为“基础列表 · 分组联系人 · 按需加载”。下面三个等宽页签分别是“基础列表”“分组列表”“懒加载”。默认第一个页签处于蓝底白字状态,内容区域标题为“消息列表”,依次显示五张白色卡片。每张卡片左侧是蓝色圆形序号,中间是“第 n 条消息”和一行具体消息,右侧为“查看”。
这个页面没有网络请求、数据库读取、下拉刷新或跳转详情。消息和联系人都是页面内置的固定文字。把运行范围看清楚,能避免两个常见误解:其一,右侧的“查看”只是当前条目的状态提示,不会打开新页面;其二,页面提到“按需加载”并不等同于已经接入了真实的数据分页或虚拟滚动。对于刚开始写 ArkTS 页面的人来说,先能准确说清一个页面做了什么,往往比先把概念名背下来更重要。
代码的起点:两组静态数组和三个可观察状态
页面文件最上方定义了两组 string[]。MESSAGES 中有“项目进度已更新”“今晚 8 点团队会议”“新的设计稿已上传”“测试环境已部署”“用户反馈待处理”五条内容;MEMBERS 中有“家人 · 爸爸”“家人 · 妈妈”“同事 · 张三”“同事 · 李四”“好友 · 小美”五条内容。它们是普通常量,不会在点击后被修改,也不需要标注状态装饰器。
真正会被用户操作改变的是页面关注的三个状态。一个状态保存正在显示的页签编号,一个状态保存被点中的消息下标,另一个状态保存第三页按钮累计增加的数量。它们都是数字,初始值也都很直观。状态发生变化时,使用它们的文字、颜色和内容会重新呈现。
tab 初始为 0,所以第一页先显示。selected 初始为 -1,而消息的下标只会是 0 到 4,因此刚启动时没有任何一条消息满足“已选中”的判断。extraCount 从 0 开始,第三页的总数文案就先显示为 10000。选择 -1 作为“不选中”的哨兵值在这种固定、从零开始的下标场景里是清晰的;它并没有额外的业务含义,也不是某个消息 ID。
把这三个状态分开是这段代码最值得保留的地方。页签切换不会改变已读的消息,点消息不会自动跳到其他页签,点击“追加 1000”也不会触碰前两页的数据。它们虽然同处一个组件,却各自只负责一类可见变化。初学者常见的写法是只声明一个笼统的对象,再把不相关的字段都塞进去;在本页这样简单的场景里,三个明确字段反而更容易沿着点击事件追踪结果。
需要同时注意,selected 只能记录一个下标。先点第 2 条,再点第 4 条时,第 2 条会恢复“查看”与蓝色圆形,第 4 条转为“已读”与紫色圆形。代码并没有“已读集合”,更没有把阅读状态写回消息数组。也就是说,画面表示的是“最近一次被点的消息”,不是一个可累积保存的消息已读功能。它足以讲清条件样式和状态驱动渲染,但不能据此推断真实聊天应用也应使用同样的数据模型。
组件树:一个可滚动页面,三段条件内容
页面外层是滚动容器,内部内容按纵向排列。最外层设置了百分百宽高和浅灰背景,内部列设置统一的左右内边距。这个组合很朴素:内容纵向排布,内容高度超过屏幕时由滚动容器承接,页面边缘留出统一的空白。
标题区域是一行横向内容。左侧放标题和副标题,占据剩余空间;右侧的蓝色小标签用于提示这是一个示例页。左侧占据剩余空间后,说明文字和标签会自然分布在行的两端,页面顶部不会显得拥挤。
标题下方又是一个 Row({ space: 8 }),其中连续调用三次 this.Tab(...)。每个 Tab 都设置 layoutWeight(1),所以三个页签会平分一行可用宽度;space: 8 只在它们之间留缝。此处没有系统自带的 Tabs 容器,完全是三个可点击文本用同一段 Builder 画出来的。因此页签的切换逻辑也很直接:点击哪一个,就把它传入的索引写给 this.tab。
第三层根据当前页签显示对应内容:第一个页签显示消息列表,第二个页签显示联系人分组,第三个页签显示数据量演示。页面只展示当前选中的一块内容,而不是把三块内容同时堆在屏幕上再隐藏。切换页签时,顶部高亮和内容区域会一起变化。
页面的可滚动性也要实事求是地理解。外层确实使用了 Scroll,所以在较小屏幕、字体放大或内容超过可视高度时,整页可以垂直滚动;但它并没有因此变成针对大量条目的 List。Scroll + Column + ForEach 很适合少量、结构简单的内容,例如这里的五条消息。数据量持续增大时,问题不是“能不能滚动”,而是每一项是否被持续创建、测量和绘制;当前页面没有针对那个问题做处理。
页签 Builder:同一套外观,三次不同的参数调用
三个页签由一段可复用的 Builder 负责绘制。它接收一个数字和一段文字,统一设置字号、字重、颜色、布局权重、居中、内边距、背景、圆角和点击行为。把共同外观集中处理的价值不在于少写几行,而在于三个页签始终采用相同的视觉和交互规则。
当前页签的编号决定样式。选中时文字加粗、使用白色并配蓝色背景;未选中时使用常规字重、灰蓝色文字和浅灰背景。点击“分组列表”后,选中编号变成第二项,第一项恢复未选中样式,第二项变成高亮,内容区域也从消息切换到联系人。无需手动修改多个控件,界面会根据状态重新呈现。这体现了声明式 UI 的实用习惯:界面是什么样,写成状态的结果;用户做了什么,限制为更新状态。
从页面行为看,页签没有禁用态、长按态、焦点态、滑动切换、动画或额外的可访问性描述。默认总是显示第一项。不要因为页面长得像选项卡,就自动补上这些未实现的能力。当前 Builder 的职责只有三项:显示名字、显示选中样式、在点击时切换索引。
还有一个容易忽略的细节:页签 Builder 与页面状态处在同一个作用域,因此可以直接读取当前选中项并在点击时更新它。如果把这段 UI 拆成独立组件,状态归属与回调接口就需要重新设计。对于只有三个页签、只在一个页面出现的场景,把 Builder 放在当前页面里是合理的局部复用;它不是一个已经抽象完成的通用导航组件。
基础列表:ForEach、稳定 key 与一条消息的状态变化
当第一项页签被选中,页面先显示“消息列表”标题,再按顺序生成五条消息卡片。每条数据都带有自己的位置标识,页面用它来识别同一项,而不是把用户看到的圆形序号当成数据身份。五个消息文本目前互不相同,也不在运行时改变,所以这组固定数据能够保持稳定对应。
需要强调这一点,是因为真实消息的标题通常不适合作为唯一标识。两条消息可能恰好有相同文案,文案也可能被编辑或本地化;如果标识重复或频繁变化,更新时就难以准确对应旧项与新项。带动态数据的消息页更适合使用不可变的消息 ID。当前页面数据固定,所以采用文本标识足以支撑这次展示,但它不是所有列表的通用规则。
每个消息项由第二个 Builder 负责绘制。整个条目是一行横向内容,宽度占满父容器,内边距 14,白色背景和 16 的圆角。左边序号区域宽高都为 38,文字居中,圆角为 19,形成圆形。数据位置从零开始,而界面编号从一开始,这是显示规则,不会改变数据位置。
圆形颜色由当前选中位置决定。未选中时为蓝色,选中时为紫色。中部列占据剩余空间,第一行是“第 n 条消息”,第二行显示具体内容,第二行使用灰色。右侧根据是否选中显示“已读”或“查看”。这段文字没有单独的点击事件,整张消息卡片才是可点击区域。
点任意卡片,当前选中位置就被替换为该项下标。因为所有消息卡片都读取同一个选中状态,新的那一项满足条件,旧的那一项不再满足条件。于是你会看到一个紫色圆形和一个“已读”,而不是五条消息分别独立保存读过与否。将这种现象亲手点一遍,比只读“状态会触发刷新”的解释更能理解状态是怎样影响多个位置的。
页面也没有阻止重复点击。已经显示“已读”的条目再次点击,selected 被赋为相同的数字,视觉上没有新的变化。这是预期内的,因为没有计数、时间戳或切换逻辑。右侧“查看”也不代表存在详情操作;从代码可知,点击整行只设置了 selected,没有导航、弹窗、打印或请求。把文案和功能分开验证,是避免读示例时想当然的重要习惯。
MessageItem 的布局为何看起来像一张完整卡片
这段 UI 没有使用图片、图标资源或自定义绘制,卡片感完全来自基本组件和尺寸关系。外层白底圆角在浅灰页面背景上形成清晰边界;固定 38 的序号圆形提供视线起点;中间列用 3 的纵向间距把标题与摘要压得较近;右侧文字以蓝色保持与序号初始色、活动页签色的呼应。Row({ space: 12 }) 让左圆与中部内容隔开,layoutWeight(1) 让中部主动占满,右侧“查看”因此靠近卡片右端。
序号区域通过顶部内边距和居中方式让数字在视觉上接近垂直居中。不同字体、字号或设备字体缩放下,视觉重心可能略有变化;页面没有额外做精确的垂直对齐校正。初学阶段可以先理解当前写法的目的,不要把截图上的效果当作所有尺寸下严格不变的保证。
消息摘要没有设置最大行数、截断方式或无数据占位。当前五条中文文本都足够短,在常规手机宽度上能落在一行。若传入很长的内容,实际布局会怎样,需要以设备和 ArkUI 的文本排版结果为准;不能从这份代码宣称“长消息一定省略”或“卡片高度一定不变”。同理,条目卡片没有分割线,间距来自外层 Column 的 space: 10,而不是每个条目自身的 margin。
这个 Builder 也说明了局部复用的边界。它依赖父组件的 selected,传入的参数只有名字和下标,正好服务当前消息区。联系人行的结构、内容和样式不一样,所以没有强行复用 MessageItem。复用不是把任何“一行”都塞进同一个组件;当两段 UI 的数据含义、状态变化和布局关系不同,保持各自清楚的代码通常更容易维护。
分组列表:名称是按字符串前缀推断出来的
当点击第二个页签,页面进入 tab === 1 的分支。它创建标题“联系人分组”,随后也对 MEMBERS 使用 ForEach。每次遍历会先画一个小标题,再画一个联系人行:
Text(item.startsWith('家人') ? '家人' : item.startsWith('同事') ? '同事' : '好友')
Text(item.split(' · ')[1])
第一句根据字符串是否以“家人”或“同事”开头决定分组;只要前两个条件都不成立,就统一显示“好友”。第二句用分隔符 · 切开字符串,取数组下标 1,即中点后面的姓名。于是“家人 · 爸爸”会显示分组“家人”和姓名“爸爸”,“同事 · 张三”会显示“同事”和“张三”。这是一份很适合解释字符串处理的教学数据,但其正确性依赖数据格式完全一致。
例如,如果某条字符串没有 ·,split 后没有下标 1,姓名区域就没有现有表达式所预期的结果;如果前缀换成“朋友”,分支也会把它归到“好友”。当前页面只定义了五条满足格式的固定内容,所以运行中不会遇到这些情况。理解依赖条件比凭空补写异常处理更有价值:页面没有加载外部数据,也没有声明格式校验,它展示的只是如何对已知字符串做最短路径的拆分。
更重要的是,当前的“分组”是逐项显示标签,并不是把联系人先整理成嵌套分组。数组顺序为家人、家人、同事、同事、好友,遍历时每一项都会先生成一次标题,因此屏幕上会依次出现“家人、爸爸”“家人、妈妈”“同事、张三”“同事、李四”“好友、小美”。“家人”和“同事”标题各重复一次。这不是截图遗漏,也不是 UI 自动合并的效果,而是 ForEach 函数体每次都包含一个标题文本的直接结果。
联系人行本身是一行横向内容:左侧姓名占据剩余空间,右侧是灰色的 ›,外层宽度百分百、内边距 14、白底、圆角 14。它没有点击事件,所以点联系人不会选中、展开或进入详情;箭头在这个页面里只是视觉符号,不是已经接通的导航按钮。若只看界面,很容易把箭头理解为“可进入下一页”,但实际页面没有提供这样的操作。
若需求真的是传统通讯录分组,数据更适合表达为“分组对象数组,分组内再有成员数组”。外层循环先渲染一次组名,内层循环渲染所有成员,标题自然不会重复。这是未来改造方向,不是当前页面已有的结构。也不应添加折叠、搜索、字母索引或联系人详情等描述,因为页面没有相应状态、控件和事件。
第三个页签:数据量文案不等于 LazyForEach
第三个页签是最需要谨慎理解的部分。页面显示浅蓝色区域,顶部一行左侧文字是“虚拟数据池”,右侧是“追加 1000”按钮。每点击一次,页面总数增加一千,组成“共 n 条数据,仅构建当前可见的列表项。”;再往下显示“第 1 条懒加载数据”至“第 4 条懒加载数据”四条固定文字。
因此,连续点击按钮确实会让总数从 10000 变成 11000、12000,界面的文案会更新;但数组没有增加任何元素,ForEach 的迭代次数也始终为四。没有一万条对象,没有数据源类,没有滚动到末尾追加数据,也没有条目回收逻辑。按钮验证的是数字状态变化与文本拼接,不是数据追加功能。四行文字也不会因为按钮点击而变成一千零四行或一万零四行。
页面文案“仅构建当前可见的列表项”与现有实现不相符。解释这一点并不是要求读者忽略第三页,而是要把它当作一个尚未完成的概念展示:它给出了“总量”和“按需”两个词,但没有接上实现它们所需的组件和数据。拿它做性能结论会产生误导。例如,当前外层虽然能滚动,但并没有根据可见范围决定哪些大数据条目创建;所以不能用它比较一万条数据时的内存占用、首屏耗时或滚动流畅度。
真正需要处理大量同构条目时,通常会选择 List 作为列表容器,并让 LazyForEach 从可按索引访问的数据源取得项目,再以 ListItem 描述单项。这些名词在这里仅用于区分概念:当前页面没有它们的调用,也没有把具体的长列表实现放进来。若以后要扩展,应先决定数据是否真的有上万条、每项是否有稳定 ID、滚动时需要怎样获取更多数据,再针对那个新需求单独设计和验证。
第三页的布局也有可直接观察的部分。外层 Column 设置了 12 的间距、16 的内边距、浅蓝背景 #EAF1FF 和 18 的圆角。最上方 Row 中,“虚拟数据池”使用 layoutWeight(1),按钮便被推向右侧;按钮高度是 38,字号 13,背景蓝色。说明文本颜色为灰蓝色。下面四个文本各自是白底、圆角 14、内边距 14,占满宽度。这里没有 ListItem,没有滚动条配置,也没有针对每项的点击处理。
ForEach 在三处的相同点与不同点
当前页面一共三处 ForEach。第一处遍历五条消息,每项生成一张消息卡片;第二处遍历五位联系人,每项生成分组标题和联系人行;第三处遍历四条“懒加载数据”,每项生成一个文本。三处都不是 LazyForEach,也都按现有数据元素生成内容。
它们的相同点是:都能让“数据数量变化时写很多重复 UI”的问题变得简洁;都需要在数据有可能变动时认真考虑 key;都把单项的内容写在遍历函数中或交给 Builder。不同点则来自数据和交互。消息项的回调同时得到下标,要用它显示序号和保存选中位置;联系人项只需要字符串,靠字符串解析得到两段文字;第三页不关心下标,也不改变它的四条数组。
不必把 ForEach 当作“只能渲染简单列表”的组件。它只是按数据集合重复创建 UI 的一种方式。适不适合继续使用,取决于数据量、容器、交互密度和性能需求。五条静态消息、五条静态联系人这样的规模,用滚动容器、纵向布局和 ForEach 直接展示,结构容易理解。若单项结构不断变复杂,可以先把单项抽成 Builder 或组件;若数量进入大规模且需要高效滚动,则需要重新选择列表容器和数据供给方式。不要因为页面标题出现“懒加载”就跳过中间这些判断。
这里还可以观察到 key 并不会出现在画面上。消息左侧显示的 1 到 5 来自 index + 1,联系人右侧的箭头是常量文本,第三页前面的闪电符号也是文本拼接。key 的目的与这些显示元素完全不同。把用户可见编号拿来充当数据唯一标识,在排序、筛选或删除后往往容易出现错位;当前数组不会变动,示例才没有暴露这个问题。
从事件到画面:逐项追踪三种状态更新
用三条实际操作可以把这个页面的状态流走一遍。第一,启动后点击“分组列表”。第二项页签被选中,标题和副标题不变,页签颜色改变,消息区域被联系人区域替换。消息选中状态和第三页的数量状态仍保持初始值,因为它们不参与当前分支的显示。
第二,切回“基础列表”,点击第 3 条消息。第 3 张消息卡片中的圆形背景变为紫色,右侧文字变成“已读”;其他四条继续保持蓝色和“查看”。随后点击第 1 条,刚才的第 3 条又回到未选中样式。这正是单个数字状态只能指向一项的体现。
第三,打开“懒加载”,点击一次“追加 1000”。页面总数从 10000 变为 11000,再点击一次变为 12000。四条示例文字不会变化,因为数量显示和这四条文字是两组独立内容。看到“总数已变化而内容数量未变化”时,不应误判为渲染故障。
这三种操作都没有跨组件的复杂通信。状态属于当前页面,Builder 从页面作用域读取状态,事件也直接更新对应字段。这是小页面容易理解的形态:页签、消息标记和第三页数字各自有明确来源。当页面发展为多个独立组件、状态来源又增加时,再考虑状态提升、参数传递或其他组织方式;目前不需要为了“看起来完整”提前增加没有用途的层次。
样式数值不是装饰,它们构成了信息层级
页面的颜色和间距虽少,却分担了明确职责。全页的 #F5F7FB 让白色卡片从背景中浮出来;主蓝 #2563EB 同时用于活动页签、序号圆形、按钮和部分文字,告诉用户哪些元素与当前操作相关;选中的消息改用紫色 #7C3AED,让它区别于普通的蓝色序号。副标题和辅助信息使用 #667085,联系人箭头使用更浅的 #98A2B3,避免与标题和动作文字争夺注意力。
字号也按内容重要程度排列。页面标题为 24 且粗体,是视觉起点;分区标题为 18 且粗体;消息标题和联系人姓名为 16 或附近尺寸;摘要、页签与按钮文字为 13 或 14。颜色直接用于各个页面元素,没有主题切换或暗色模式适配。对于这个小页面,这样能让读者一眼看到各层信息的区别。
圆角数值也有层次:消息卡片 16,联系人行和第三页条目 14,第三页容器 18,页签 10,序号标签 12,序号圆形 19。并不需要从这些数值推导出严格的设计规范;它们的作用是让容器在视觉上有差别。若调整其中一个值,应该在设备上重新查看边缘、空间和点击区域。当前页面没有针对不同屏幕尺寸的特殊分支,验证应覆盖实际要支持的设备。
还要注意,外观属性的先后并不改变页面状态。字体颜色、背景色和圆角只负责视觉呈现,真正触发切换的是点击动作对状态的更新。理解页面时,先把“哪个状态决定什么”列清,再看各状态对应的样式,通常会比只看颜色更容易掌握交互关系。
这个示例没有实现什么,以及为什么必须写清楚
从实际交互可以确定,当前页面没有 List、ListItem、LazyForEach、动态数据源、接口请求、分页参数、下拉刷新、上拉加载、删除条目、筛选、搜索、联系人跳转、消息详情或持久化已读状态。点击“追加 1000”也不会真的生成一千项。每一种能力都需要额外的数据、状态或事件,当前页面没有提供这些入口。
这并不让页面失去学习价值。相反,它把一个最小的可运行界面放在眼前:固定数组如何遍历,状态如何控制条件分支,点击如何改变样式,字符串如何做一次简单解析,滚动容器如何包住整页。能先把这些小点看明白,后续接入真实数据时才知道应该替换哪一层,而不是把所有概念混成“列表会自己变快”。
接口、缓存、埋点、权限和复杂架构都不属于当前五条静态数据的交互范围。真正的扩展应由新的需求启动。例如需要消息详情,就要定义点击后进入哪里和传递什么数据;需要累积已读,就要把单个选中位置改成可表达多项状态的数据;需要大数据滚动,才设计 List、LazyForEach 与稳定 ID。每一步都应当有新的实现和运行验证。
页面图片只用于帮助读者辨认初始布局和操作后的界面位置。初始图对应默认的消息列表;操作图也只作为页面视觉参考,不把图片中的静态内容解释成额外功能。文章中的结论仍以实际点击后的文字、颜色和数量变化为准。
在设备上逐项验证
下面的检查不需要修改页面内容,目的是把每一条关于当前交互的描述落到可观察结果上。
-
运行应用。确认启动后进入标题为“列表与懒加载”的页面,默认高亮的是“基础列表”。
-
查看基础列表。确认有五张消息卡片,左侧数字从 1 到 5,右侧初始都为“查看”,消息文字按固定顺序出现。
-
点击任意一张消息卡片,例如第 3 条。确认该条圆形编号变紫,右侧显示“已读”。再点另一条,确认新条目成为已读,旧条目恢复查看。这个现象说明页面只保留最近一次选中的位置。
-
点击“分组列表”。确认第二个页签变为蓝底白字,内容标题改为“联系人分组”。逐项向下查看,会发现“家人”和“同事”的标签跟随各自联系人重复出现。这正是当前遍历函数每次都创建分组标题的结果,不应期待自动合并。
-
点击联系人行。确认当前页面没有选中颜色、详情页或弹窗出现。右侧箭头只是文本符号;这一步用来排除“箭头等于已实现导航”的误读。
-
点击“懒加载”。确认有“虚拟数据池”、一个“追加 1000”按钮、一句总数说明和四条白色示例项。点击按钮两次,确认数字以 1000 为步长增加两次,而四条示例项的数量和文字不变。这说明页面没有真的追加一千条数据。
-
旋转或换用较小的设备时,观察页面能否纵向滚动、标题和卡片是否仍可见。这里记录实际设备结果即可,不要根据某个截图推断所有屏幕都有相同排版。
-
回到三个页签,重复观察消息、联系人和四条示例数据。若要判断是否存在真正的懒加载,应以条目数量是否随总数增加、滚动时是否出现新内容为依据;当前页面的四条示例项始终不变。
读完这一页后,应该带走什么
这个页面的价值并不在于展示一个规模庞大的高性能列表,而在于把三类非常基础的页面行为放到一张可操作的界面中。第一页说明固定消息如何变成重复卡片,以及一个选中状态怎样同时驱动颜色和文案;第二页说明同样的遍历也可以结合字符串判断和拆分来组织显示;第三页提醒我们,数据量的文字、按钮的计数变化和真正的按需构建是三件不同的事。
如果你正在学习 ArkTS,可以按“看默认状态—切换页签—点击条目—回看变化”的顺序体验它。先记住当前页签决定哪块内容出现,选中状态决定哪条消息呈现已读,第三页数量决定总数文字;再看两个复用区域如何保持共同外观。这样体验后,不仅能复述页面样子,也能解释每个变化为什么发生。
如果之后确实要把它改造成真实的长列表,需要重新准备结构化数据、选择合适的列表容器、定义稳定标识、验证滚动过程和数据追加。当前页面没有完成那一步,所以这里不把设想描述成现有功能。准确地理解一个小示例,是走向复杂页面的可靠起点。
一次针对这张页面的观察练习
面对内容不多的页面,可以先只看界面,预测启动时会出现哪一页、哪条消息会被标成已读、第三页数字是多少;再运行应用,逐项比对预测。这个过程能检验自己理解的是状态关系,还是只记住了组件名称。
接着从每个可点击元素倒推行为。顶部三个文字采用相同的页签样式,因此点击任意一个都只切换当前内容;消息区看起来有五个“查看”,但它们的交互规则一致,点击整行后只会标记最近一条,不会跳转。这种按页面区域观察的方式,能避免在重复卡片中迷路。
第三页则适合练习区分总数文字和具体 UI 项。点击“追加 1000”后,总数说明会增加一千,但下方四条示例项不变。这样一步一步观察,按钮的真实影响范围就很明确:它影响一句文本,不影响示例项个数。若只从“追加 1000”四个字推断功能,很容易把界面文案误当成实现。
还有一个实用的观察是把默认画面和点击结果逐一对比。启动时第一页被选中,消息没有已读标记,第三页总数是 10000;点第 5 条消息后,只有它变紫并显示已读;点“追加 1000”后,总数变成 11000。这样的对照比笼统地说“界面会更新”更具体。
这个练习同样提醒我们不要超出页面证据。页面没有网络失败提示、长文案专门排版、深色模式或性能指标。可以如实记录当前设备上看到的内容,却不应把局部观察扩写成产品承诺。已实现的地方说清细节,没有实现的地方说明边界,下一步若要扩展则需要新的设计和验证。
最后可以试着用一句话概括每个页签。基础列表是“由五条固定消息生成、可选中一条的消息卡片”;分组列表是“按文字前缀显示类别、类别标题随联系人重复的联系人行”;第三页是“总数文字可以增加、但始终显示四条固定示例项的演示区”。当这三句概括都能被页面操作直接验证时,就已经掌握了这张页面最重要的内容。之后再学习真正的 List 或 LazyForEach,也更容易看清哪些是新加入的容器、数据源和性能行为。
三个页签之间的连续体验
三个页签虽然展示不同内容,但共享相同的顶部结构和页面背景。用户从基础列表切到分组列表时,标题区域不会消失,只有下方内容区域改变;再切到懒加载时,顶部仍保持原来的位置,第三个页签通过蓝底白字提示当前所在区域。这样安排让用户不会因为内容更换而失去方向,也不会误以为每次点击页签都重新打开了一个完全独立的页面。
页签切换还可以用来观察状态是否被意外重置。先在基础列表选中第 2 条,再查看分组列表,联系人行不会出现消息的紫色标记;回到基础列表后,第 2 条仍然是已读。再进入懒加载并点击一次追加按钮,回到基础列表时,消息的选中结果不会被清除,第三页的总数也不会倒退。这些结果说明不同状态各自服务自己的区域,页签只是改变当前可见内容。
如果用户重复点击当前已经选中的页签,画面不会增加新的卡片,也不会把消息状态清空。重复点击“分组列表”不会重新生成一套不同联系人,重复点击“懒加载”页签也不会自动增加总数;只有点击页签内部的“追加 1000”按钮,数字才会变化。把导航动作和内容动作分开,是这张页面保持可预测性的原因之一。
第三页的总数文字即使增加到很大的数字,页面下方仍只有四条固定项,因此它适合观察数字反馈,不适合用来判断大量数据滚动性能。用户可以连续点击按钮,看到 10000、11000、12000 依次变化,同时看到四条白色条目的位置和文字不变。这个对照很直白:上方数字表达一个演示中的总量,下方条目表达当前真正放在屏幕上的内容,两者不能互相替代。

更多推荐




所有评论(0)