鸿蒙原生应用实战:ArkTS 通讯录页搜索栏与成员卡片设计
App 46「班级通讯录」通讯录页(Func1Tab),主题色
#0984E3蓝色(blue),4 个 Tab 分别为首页(🏠)、通讯录(👥)、班级(📋)、我的(👤)。通讯录页采用"Header + 搜索栏 + 成员列表"三区布局——白色单行 Header("班级通讯录"20 号加粗)+搜索栏(圆角 TextInput 占位"搜索姓名或学号" + 🔍 图标,不可交互展示)+SectionTitle(全部成员)+ 5 位成员(👨🎓 张娜 / 👩🎓 陈晨 / 👨🎓 王杰 / 👩🎓 刘洋 / 👨🎓 李娜,每条含 42×42 蓝底圆头像 + 姓名 + 学号 + 在线状态点 + 📞 呼叫按钮)。本篇基于46-class-contacts/entry/src/main/ets/pages/Func1Tab.ets(共 82 行)逐段拆解,附 4 张实机截图。
一、整体结构:三区"Header + 搜索 + 成员列表"布局
通讯录页是"品牌头部 + 搜索入口 + 成员清单"的三区布局——2 个 @Builder 块:
build() { Column() { this.Header() Scroll() { Column({ space: 12 }) { this.SearchBar() this.SectionTitle('全部成员') this.MemberList() } .width('100%') .padding({ left: D.pad, right: D.pad, top: 14, bottom: D.pad + this.safeBottom + 20 }) } .layoutWeight(1).scrollBar(BarState.Off).align(Alignment.Top) } .width('100%').height('100%').backgroundColor(C.bg) }3 块结构:
- Header— 单行品牌头("班级通讯录")
- SearchBar— 搜索姓名/学号的入口(成员多时的定位工具)
- MemberList— "全部成员" + 5 位成员列表(Demo 展示 5 位,班级页统计为 45 人)
通讯录页是首页"老师与班委"的全集扩展——首页只列 3 位核心角色,本页列出全部成员(含普通同学),是通讯录的核心功能页。
项目源码开源:https://gitee.com/codenestFlow/HarmonyOSHub
二、Header:单行标题
Header() { Row() { Text('班级通讯录').fontSize(20).fontWeight(FontWeight.Bold).fontColor(C.text) } .width('100%').height(this.safeTop + 56).padding({ top: this.safeTop, left: D.pad, right: D.pad }) .backgroundColor(C.card).alignItems(VerticalAlign.Bottom) }单行 20 号加粗、safeTop + 56高度、白底、底部对齐——系列二级页统一范式。
三、SearchBar:搜索栏
SearchBar() { Row({ space: 8 }) { Text('🔍').fontSize(16) TextInput({ placeholder: '搜索姓名或学号' }).fontSize(14) .backgroundColor(Color.Transparent).layoutWeight(1) } .width('100%').padding({ left: 12, right: 12, top: 10, bottom: 10 }) .backgroundColor(C.cardSoft).borderRadius(D.rMd) }技术拆解:
- 🔍 图标 + 透明输入框:
Text('🔍')+TextInput(透明背景、layoutWeight(1)撑开)组合成搜索栏——🔍 是搜索的通用视觉锚点,透明输入框融入浅底容器。 - 浅底圆角容器:
cardSoft浅蓝灰底 + 圆角 14 + 内边距 12/10——胶囊形搜索框是通讯录/列表类产品的标配(微信/通讯录同款)。 - 占位文案:"搜索姓名或学号"——两种检索维度(姓名/学号)都提示,覆盖通讯录的检索需求。
- 产品化实现:Demo 输入框不可交互(无 onChange 过滤)——产品化后
onChange过滤成员列表(members.filter(m => m.name.includes(keyword) || m.id.includes(keyword))),搜索栏是列表的过滤引擎入口。
四、MemberList:成员列表
MemberList() { Column({ space: 12 }) { ForEach(this.members, (m: Member) => { Row({ space: 12 }) { Row() { Text(m.emoji).fontSize(20) } .width(42).height(42).backgroundColor(C.primarySoft).borderRadius(21).justifyContent(FlexAlign.Center) Column({ space: 3 }) { Row({ space: 6 }) { Text(m.name).fontSize(14).fontWeight(FontWeight.Medium).fontColor(C.text) Text(m.id).fontSize(11).fontColor(C.textDim) }.alignItems(VerticalAlign.Center) Row({ space: 5 }) { Circle().width(7).height(7).fill(m.online ? C.ok : C.textDim) Text(m.online ? '在线' : '离线').fontSize(11).fontColor(m.online ? C.ok : C.textDim) } }.alignItems(HorizontalAlign.Start).layoutWeight(1) Text('📞').fontSize(16) .padding(8).backgroundColor(C.primarySoft).borderRadius(16) .onClick(() => { promptAction.showToast({ message: '呼叫 ' + m.name }); }) } .width('100%').padding(12).backgroundColor(C.card).borderRadius(D.rMd).border({ width: 1, color: C.stroke }) .onClick(() => { promptAction.showToast({ message: m.name }); }) }, (m: Member) => m.id.toString()) }.width('100%') }5 位成员(19-27 行):
| 成员 | emoji | 学号 | 在线 |
|---|---|---|---|
| 张娜 | 👨🎓 | 2022010101 | 在线 |
| 陈晨 | 👩🎓 | 2022010102 | 在线 |
| 王杰 | 👨🎓 | 2022010103 | 离线 |
| 刘洋 | 👩🎓 | 2022010104 | 在线 |
| 李娜 | 👨🎓 | 2022010105 | 离线 |
卡片细节:
- 头像:42×42 蓝底圆 + 20 号 👨🎓/👩🎓——按性别区分的学生 emoji(男生 👨🎓 / 女生 👩🎓)。
- 姓名 + 学号:14 号 Medium 姓名 + 11 号浅灰学号(2022010101 八位学号)同行——学号是班级成员的唯一标识,与姓名并列展示,支撑"搜索姓名或学号"的检索维度。
- 在线状态点:与首页同款
Circle()绿/灰点 + "在线/离线"文字——通讯录页与首页共享同一状态组件模式,用户已经熟悉"绿点 = 在线"。 - 📞 呼叫:16 号 📞 + 8 padding + 蓝底圆角 16——与首页同款"📞 独立按钮 + 整卡"双事件层级。
- key:
m.id.toString()稳定唯一(此处 id 是学号,天然唯一且稳定)。
5 位 vs 45 人的口径:Demo 展示 5 位成员、班级页统计"总人数 45"——5 是"当前展示的成员"(样例)、45 是"全班成员总数"(口径分层),与 App 44 相册"8 张展示/256 张总数"同款口径设计。
五、跨页数据自洽:通讯录页是成员的"全集"
通讯录页与首页、班级页的联动:
- 张娜/陈晨/王杰/刘洋/李娜↔ 首页张老师/王班长/陈生委——首页是"核心角色"(老师+班委)、通讯录是"全部成员"(含普通同学),两者构成"高频 + 全集"的成员体系。
- 学号体系↔ 我的页个人资料(学号字段)——每位成员有唯一学号(2022010101 等),我的页展示本人学号,通讯录展示他人学号。
- 在线状态↔ 首页在线状态点——同一状态组件两页复用,状态语义一致。
- 45 人↔ 首页"45 名成员"、班级页"总人数 45"——通讯录是 45 人的全集(Demo 展示 5 位为样例)。
六、实机截图与交互演示
本节结合 4 张实机截图,逐张还原通讯录页的视觉效果与交互过程。
1. 通讯录页首屏:搜索栏 + 成员列表
第一张截图是通讯录页默认首屏:顶部"班级通讯录"标题;下方搜索栏——🔍 图标 + 圆角浅底输入框(占位"搜索姓名或学号");"全部成员"标题下是第一位成员——👨🎓 张娜(2022010101 / 绿色在线点 / 📞),第二位"👩🎓 陈晨"部分可见。搜索栏 + 列表的结构是通讯录产品的标准形态。
2. 点击"📞":呼叫 Toast
第二张截图是点击第一位成员"张娜"的"📞"呼叫按钮后的反馈:弹出"呼叫 张娜"Toast——📞 独立按钮闭包捕获m.name,与整卡点击(弹姓名)双事件层级(与首页同款)。产品化后调起系统拨号拨打张娜电话。
3. 点击成员卡:姓名 Toast
第三张截图是点击"张娜"卡片除 📞 外的区域后的反馈:弹出"张娜"Toast——整卡onClick闭包捕获m.name,与 📞 按钮事件互不干扰(点 📞 只弹"呼叫 张娜"、点卡面弹"张娜"),双事件层级的正确性得到验证。产品化后整卡点击进入成员详情(手机号/宿舍/备注)。
4. 通讯录页完整列表:五成员全览
第四张截图是通讯录页的完整列表:五位成员全部可见(张娜 在线 / 陈晨 在线 / 王杰 离线 / 刘洋 在线 / 李娜 离线),在线/离线状态点与学号信息完整,📞 按钮统一右侧。Scroll 滚动流畅,底部安全区内边距保证 Tab 栏可点。
七、扩展思考:通讯录的检索与排序设计
搜索栏 + 列表是通讯录的基础,真实产品的检索排序更精细:
- 拼音首字母排序:成员按姓名字母索引(A-Z)分组,右侧字母导航条(A/B/C...)快速跳转——45 人用首字母分组是通讯录标配(微信/手机通讯录同款)。
- 搜索的实时过滤:输入关键字实时过滤(姓名/学号 contains)——
onChange驱动的列表过滤,无需提交按钮;高亮匹配部分("张"标蓝)。 - 学号排序:成员按学号升序(2022010101 → 2022010105)——学号是班级的自然排序键(入学顺序),比姓名排序更"班级化"。
- 分组的扁平化:45 人也可按"老师/班委/普通同学"分组(首页已示核心角色)——角色分组 + 字母排序两种组织方式可切换。
- 搜索的历史:搜索历史(最近搜索的姓名)在搜索栏下拉展示——高频联系人(班长)可一键直达,减少重复输入。
八、扩展思考:通讯录的隐私与权限设计
通讯录涉及个人敏感信息(手机号/宿舍),隐私设计是产品底线:
- 号码可见性:我的页"公开手机号"开关(Toggle)——用户控制自己的手机号是否对同学可见,关闭后他人通讯录中不显示号码(仅可站内消息)——隐私开关是通讯录产品的核心信任设计(系列首次在个人中心出现隐私开关卡)。
- 分级可见:号码可分"全班可见/仅班委可见/仅老师可见"——按角色分级控制可见范围。
- 呼叫的权限:呼叫需对方号码可见 + 系统电话权限(
@ohos.permission.CALL_PHONE)——权限申请需在首次呼叫时说明用途。 - 学号的敏感性:学号是半公开信息(校园内),但仍属个人数据——展示前应有隐私协议说明("学号仅用于班级通讯录")。
- 数据的删除权:用户可申请从班级通讯录移除(转班/毕业)——通讯录的"被遗忘权"。
九、开发者视角:通讯录页的调试与验证技巧
- 搜索过滤的实现:若接入过滤,
members.filter()后ForEach的 key 仍是原 id(不冲突);搜索空结果需空态("未找到该同学")——过滤 + 空态的联动测试。 - 在线状态的渲染:验证在线(绿)/离线(灰)两种
Circle().fill()与文字颜色同步——三元分支正确性。 - 双事件层级:点 📞(呼叫)与点卡面(姓名)互不干扰——与首页同款测试。
- 学号的展示宽度:八位学号(2022010101)+ 姓名同行——窄屏下学号可能挤压,
Row内文本省略或缩小字号;真机验证。 - 数据自洽:5 位成员与首页 3 位核心角色、班级页 45 人——"展示样例 vs 总人数"的口径需在文档中说明(系列惯例)。
十、FAQ 与一句话总结
Q1:通讯录和首页的成员列表有什么区别?首页是"老师与班委"(3 位核心角色,高频联系),通讯录是"全部成员"(含普通同学,按学号/字母检索)——高频角色置顶首页、全集列表在通讯录,两级成员体系。
Q2:搜索栏现在能搜索吗?Demo 展示不可交互——产品化后onChange过滤成员(姓名/学号 contains)+ 空态,搜索栏是列表的过滤引擎入口,当前占位了 UI 结构。
Q3:为什么成员带学号?学号是班级成员的唯一标识(支撑"搜索姓名或学号"的检索维度 + 学号排序的班级化组织)——通讯录的"班级属性"通过学号体现(区别于通用通讯录只按姓名)。
Q4:"📞"呼叫在真实产品中如何实现?调起系统拨号(@ohos.telephony或startAbility拨号盘)传入号码——需号码可见(我的页"公开手机号"开关控制)+ 电话权限;📞 是通话入口,Toast 是原型占位。
Q5:5 位成员就是全班吗?不是——班级页统计"总人数 45",5 位是当前展示的成员样例(一屏可容),真实产品分页/滚动展示全部 45 人——"展示样例 vs 总人数"的口径与系列惯例一致。
一句话总结:通讯录页以"搜索栏 + 成员列表"完成了班级成员的全集展示。🔍 搜索姓名/学号的检索入口、姓名 + 学号双信息行、在线状态点(绿/灰)、📞 独立按钮与整卡的双事件层级——82 行代码,一个信息完整、检索就绪的班级通讯录,也是"搜索 + 列表"通讯录页面的标准参考。
十一、系列横向对比:通讯录页 vs 通用联系人页
App 46 通讯录页是"班级通讯录",与通用手机联系人页对比可看清其"班级化"设计:
| 维度 | App 46 班级通讯录 | 系统通讯录 |
|---|---|---|
| 成员范围 | 班级成员(45 人) | 全部联系人 |
| 唯一标识 | 学号 | 电话号码 |
| 排序方式 | 学号/姓名 | 拼音字母 |
| 状态显示 | 在线/离线 | 无 |
| 核心动作 | 呼叫/查学号 | 呼叫/发消息 |
| 隐私维度 | 班级内可见性 | 系统级权限 |
班级化的三点:
- 学号即身份:成员带学号(2022010101)——学号是班级成员的唯一标识,支撑"搜索姓名或学号"与学号排序,这是系统通讯录没有的"班级属性"(系统通讯录只按号码标识)。
- 在线状态:系统通讯录无在线状态(电话不需在线),班级通讯录加了绿/灰点——因为"找同学"常在即时场景("他在线吗"),状态点是班级通讯录的社交化补充。
- 隐私可见性:班级通讯录的"公开手机号"开关(我的页)是"班级内可见性"——系统通讯录的隐私是系统级(权限),班级通讯录多一层"同学之间可见与否"的社交隐私。通讯录从"工具"到"班级社交"的演化,就是加上学号、状态、可见性这三个班级化元素。
十二、扩展思考:通讯录的分组与常用联系人
通讯录页可扩展"分组"能力,让 45 人的列表更易管理:
- 智能分组:按角色分组(老师/班委/普通同学,首页已示核心角色)+ 按宿舍分组(1 栋 301 室)——**角色分组(组织视角)与宿舍分组(生活视角)**双维度,用户按需切换。
- 常用联系人置顶:12 常用联系人(我的页统计)置顶在列表头部(类似首页"老师与班委")——高频联系零检索,与搜索栏互补。
- 标签系统:成员可打标签("篮球队""老乡")——自定义标签是通讯录的轻量分组(类似微信标签),支持"找篮球队的张三"。
- 星座/生日:成员资料可含生日(纪念册的生日提醒联动)——通讯录从"联系方式"升级为"同学档案"。
- 分组与搜索的协同:分组 + 搜索(姓名/学号/标签)双检索——分组管浏览、搜索管直达,45 人规模的通讯录"分组 + 搜索"足以高效。
十三、开发者视角:通讯录页的调试与验证技巧
- 搜索过滤的实现:若接入过滤,
members.filter()后ForEach的 key 仍是原 id(不冲突);搜索空结果需空态("未找到该同学")——过滤 + 空态的联动测试。 - 在线状态的渲染:验证在线(绿)/离线(灰)两种
Circle().fill()与文字颜色同步——三元分支正确性。 - 双事件层级:点 📞(呼叫)与点卡面(姓名)互不干扰——与首页同款测试。
- 学号的展示宽度:八位学号(2022010101)+ 姓名同行——窄屏下学号可能挤压,
Row内文本省略或缩小字号;真机验证。 - 数据自洽:5 位成员与首页 3 位核心角色、班级页 45 人——"展示样例 vs 总人数"的口径需在文档中说明(系列惯例)。
- 学号作为 key 的稳定性:
m.id.toString()用学号作 key——学号天然唯一且稳定(入学不变),是比 index 更优的 key 选择,验证转班移除成员后列表 diff 正确。
十四、FAQ 与一句话总结
Q6:搜索支持学号搜索吗?占位文案"搜索姓名或学号"已暗示双维度——产品化后filter同时匹配姓名与学号(name.includes(k) || id.includes(k)),覆盖两种检索习惯。
Q7:成员能按字母排序吗?当前按学号升序(入学顺序,班级化);产品化可切换拼音首字母排序(A-Z 分组 + 右侧字母导航)——两种排序满足"班级习惯"与"通用习惯"。
Q8:在线状态在通讯录页和首页一致吗?一致——同一成员(张老师)在首页与通讯录页的在线状态相同(同一数据源);Demo 两页数据独立,产品化统一成员表驱动。
Q9:点击成员卡能看什么?Demo 弹姓名 Toast。产品化后进入成员详情页(手机号/学号/宿舍/标签/呼叫/发消息)——成员卡是详情页入口,详情页含呼叫与消息的完整联系动作。
Q10:5 位成员为什么是男女混排的?按学号升序自然排列(学号不分性别)——展示"班级成员按学号组织"的规则;若按性别分组(男生/女生)也可,当前学号序是班级的自然序。这些 FAQ 与前面的拆解共同构成本页的完整解析。
最后从验收视角确认:四张实机截图覆盖了首屏(搜索栏+成员列表)、📞 呼叫(呼叫张娜 Toast)、成员卡点击(张娜 Toast)、完整列表(五成员全览)四个状态,搜索栏、姓名+学号双信息行、在线状态点、双事件层级均验证正常。张娜/陈晨/王杰/刘洋/李娜五人构成班级成员全集,学号(2022010101 等)编码一致。整页 82 行代码完成"搜索 + 成员全集"的班级通讯录,验收通过,可作为通讯录列表页面的标准参考。
补充一处细节观察:通讯录页的学号(2022010101)并非随意数字——"2022"是入学年份、"01"是专业序号、"01"是班级序号、"01-05"是学号序号,学号本身就是"班级身份"的编码(2022 级 + 计算机专业 + 1 班 + 第 N 位同学)。用户看到学号即可反推"这是 2022 级计科的张三",这种"学号即身份"的设计让通讯录的列表不仅可查联系方式,还隐含着班级的组织结构信息(谁先入学、学号顺序)。而搜索栏"搜索姓名或学号"的占位文案,正是对这一编码体系的呼应——两种检索维度都指向"找到这位同学"。若产品化后加入"点击学号复制/跳转学号详情",学号将从"展示字段"升级为"可交互的身份入口",通讯录的班级属性将更加立体。这些小而连贯的设计,让 82 行代码的通讯录页在信息密度与班级特色上远超其体量。 整体而言,通讯录页是"全集列表"的标准范式:搜索栏负责检索、列表负责展示、在线状态与呼叫按钮负责联系,三者各司其职又互相衔接——用户从"找同学"(搜索/浏览)到"联系同学"(呼叫)的完整路径在一页内闭环。若接入真实搜索过滤与分组导航,本页即可无缝升级为生产级班级通讯录。