鸿蒙原生应用 HarmonyOS ArkTS实战:不引地图库 —— 自行车地图页的模拟地图与停车点列表
鸿蒙原生应用 HarmonyOS ArkTS实战:不引地图库 —— 自行车地图页的模拟地图与停车点列表
App 33「校园自行车租赁」地图(Func1Tab),主题色
#2D9CDB青蓝。地图页采用"Header + 模拟地图 + 停车点列表"三区布局——白色 Header("车辆地图"标题) + 🗺️ 模拟地图卡(180 高浅青圆角,44 号地图 emoji + "40 辆车分布在 4 个停车点"文字) + SectionTitle + 4 个停车点列表(📍 南门停车点 80m·12 辆可用 / 📍 教学区停车点 210m·6 辆可用 / 📍 宿舍区停车点 350m·18 辆可用 / 📍 运动场停车点 480m·4 辆可用,每项含"导航 >"点击入口)。本篇基于33-campus-bike/entry/src/main/ets/pages/Func1Tab.ets(约 85 行)逐段拆解,附 4 张实机截图。
一、整体结构:三区"Header + 地图 + 停车点"布局
App 33 地图页是"标题 + 模拟地图 + 停车点列表"的简洁地图页——4 个 @Builder 块:
build() {
Column() {
this.Header()
Scroll() {
Column({ space: 14 }) {
this.MapCard()
this.SectionTitle('停车点分布')
this.SpotList()
}
.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 — 白色背景 + "车辆地图"标题
- MapCard — 模拟地图卡
- SectionTitle('停车点分布') + SpotList — 4 个停车点列表
与 App 32 地图页对比:App 32 是"Header + 地图 + 图例 + 5 伞点"四区、App 33 是"Header + 地图 + 4 停车点"三区——"33 没有图例"——"为什么 33 没有图例?"——32 伞点有 3 种类型(借/还/借还)需要图例、33 停车点只有 1 种类型(停车点)不需要图例——"图例 = 类型多样才需要"——"三区 vs 四区"——"信息复杂度决定区块数"。
与 App 33 首页对比:首页是"双行 Header + 3 统计 + 6 车 Grid"、地图是"Header + 地图卡 + 4 停车点"——"首页看车、地图看位置"——"两种视角"——"车辆维度 vs 位置维度"——"首页按车型分、地图按位置分"。
主题色 #2D9CDB 青蓝——同首页——"系列主题色的一致性"——首页/地图/骑行/我的四 Tab 共用青蓝——"品牌色贯穿全 App"。

二、Header:白色 + "车辆地图"
Header 是 **白色 + "车辆地图"**的极简标题头:
@Builder
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)
}
Header 结构:白色背景 + 20 号加粗黑色"车辆地图" + 56 高 + safeTop + 底部对齐——"同 App 32 地图 Header"。
"车辆地图" 的命名——"车辆 + 地图"——"车辆地图 = 车的位置"——与 App 32 "伞点地图"对比——"车辆地图 vs 伞点地图"——"业务对象的命名"——伞点(借伞点)vs 车辆(自行车)——"地图的主题"。
height(this.safeTop + 56) 固定高度——"56 + safeTop = 标准 Header 高"——与 App 32 相同——"系列 Header 统一高度"。
alignItems(VerticalAlign.Bottom) 底部对齐——"文字在底部"——"避开状态栏"——"标准状态栏适配"。
与首页 Header 对比:
- 首页:Column 双行("校园骑行" + slogan)
- 地图:Row 单行("车辆地图")
"双行 vs 单行"——"首页品牌、地图功能"——"首页强调品牌、地图强调功能"——"Header 形态 = 页面性质"。
三、MapCard:🗺️ 模拟地图卡
MapCard 是 浅青背景 + 🗺️ + 文字的模拟地图占位卡:
@Builder
MapCard() {
Stack({ alignContent: Alignment.Center }) {
Column().width('100%').height(180).backgroundColor(C.primarySoft).borderRadius(D.rLg)
Column({ space: 8 }) {
Text('🗺️').fontSize(44)
Text('40 辆车分布在 4 个停车点').fontSize(13).fontColor(C.primary)
}
}
.width('100%').onClick(() => { promptAction.showToast({ message: '打开地图' }); })
}
MapCard 结构:Stack 容器(背景 180 高浅青 + 居中 🗺️ 44 号 + 文字)——"占位地图的极简设计"。
height(180) 180 高——"比 App 32 的 160 高"——"33 地图更高"——"更高 = 更显眼"——"地图的视觉重量"。
Text('🗺️').fontSize(44) 44 号地图 emoji——"比 32 的 40 号大"——"44 号 = 更大图标"——"地图图标的视觉"。
Text('40 辆车分布在 4 个停车点').fontSize(13).fontColor(C.primary) 13 号青蓝文字——"地图说明"——"40 辆 + 4 停车点"——"车辆分布总览"——"数字信息"——"13 号青蓝"。
40 辆车分布在 4 个停车点 的数据信息——"40 = 总车辆数"——"4 = 停车点数"——"总览信息"——"地图文字 = 数据概览"——与首页 3 统计呼应(5+1 可用 + 36 累计)——"跨页数据"——"demo 的数据一致性"。
Stack({ alignContent: Alignment.Center }) 居中叠加——"背景 + 前景"——"Stack 的层叠"——"居中对齐"。
onClick(() => promptAction.showToast({ message: '打开地图' })) 点击反馈——"整卡可点"——"占位跳转"——"真实产品跳 Map Kit"。
与 App 32 MapCard 对比:
- App 32:160 高 + 🗺️ 40 号 + "校园伞点分布图"
- App 33:180 高 + 🗺️ 44 号 + "40 辆车分布在 4 个停车点"
"两个地图卡的差异"——"33 更高更大"——"33 文字带数据(40 辆 4 点)"——"数据化地图卡"——"32 文字是标题、33 文字是数据"——"占位卡的演进"。

四、SpotList:4 个停车点(含车辆数 + 导航)
SpotList 是 4 个停车点的列表——每项含 📍 emoji、名称、"距离·车辆数"、"导航 >":
@Builder
SpotList() {
Column({ space: 10 }) {
ForEach(this.spots, (s: Spot) => {
Row({ space: 12 }) {
Text(s.emoji).fontSize(22)
Column({ space: 3 }) {
Text(s.name).fontSize(14).fontColor(C.text)
Text(s.dist + ' · ' + s.count + ' 辆可用').fontSize(12).fontColor(C.textDim)
}.alignItems(HorizontalAlign.Start).layoutWeight(1)
Text('导航 >').fontSize(12).fontColor(C.primary)
.onClick(() => { promptAction.showToast({ message: '导航: ' + s.name }); })
}
.width('100%').padding(14).backgroundColor(C.card).borderRadius(D.rMd).border({ width: 1, color: C.stroke })
}, (s: Spot) => s.id.toString())
}.width('100%')
}
每张停车点卡的结构:
- 左 📍 emoji(22 号)
- 中 文字区(名称 14 号 + "80m · 12 辆可用" 12 号灰)
- 右 "导航 >"(12 号青蓝)
4 个停车点(每点 4 要素):
| 📍 | 名称 | 距离 | 车辆数 |
|---|---|---|---|
| 📍 | 南门停车点 | 80m | 12 辆 |
| 📍 | 教学区停车点 | 210m | 6 辆 |
| 📍 | 宿舍区停车点 | 350m | 18 辆 |
| 📍 | 运动场停车点 | 480m | 4 辆 |
Text(s.dist + ' · ' + s.count + ' 辆可用') 距离 + 车辆数——"80m · 12 辆可用"——"· 中点分隔"——"距离 + 库存一行"——"紧凑信息"——"12 号灰"。
Text('导航 >') 导航按钮——"12 号青蓝"——">" 箭头——"跳转方向"——"点击导航"。
onClick(() => promptAction.showToast({ message: '导航: ' + s.name })) 点击反馈——"导航: 南门停车点"——"占位导航"——"真实产品调起地图 App"。
padding(14).borderRadius(D.rMd).border({ width: 1, color: C.stroke }) 14 padding + 14 圆角 + 1px 边框——"列表卡标准"。
与 App 32 SpotList 对比:
- App 32:5 伞点 + emoji 圆点颜色(🔵🟢🟡)+ 类型文字
- App 33:4 停车点 + 📍 emoji + 距离 + 车辆数
"两个列表的差异"——"32 按类型分色、33 统一 📍"——"33 更简洁(只有 1 种类型)"——"停车点没有类型差异"——"信息字段的差异"——"32 关注类型、33 关注数量"。
4 停车点的"位置逻辑":
| 停车点 | 距离 | 车辆 | 场景 |
|---|---|---|---|
| 南门 | 80m | 12 | 入口(近 + 多) |
| 教学区 | 210m | 6 | 学习区 |
| 宿舍区 | 350m | 18 | 生活区(最多) |
| 运动场 | 480m | 4 | 运动区(最少) |
"4 停车点的场景覆盖"——"南门/教学/宿舍/运动"——"校园的 4 类区域"——"入口/学习/生活/运动"——"场景的全覆盖"——"停车点的布局逻辑"。
"宿舍区 18 辆最多"——"宿舍 = 骑行高频"——"学生宿舍人多"——"宿舍停车点最大"——"车辆数的业务逻辑"——"按需求分配车辆"。
"南门 12 辆"——"南门 = 出入口"——"进出校园骑行需求"——"入口停车点多"——"位置与数量的关联"。
"运动场 4 辆最少"——"运动场 = 低频骑行"——"去运动场走路或骑车都行"——"车辆数最少"——"需求的量化"。
Spot 模型(5 字段):
interface Spot {
id: number;
emoji: string;
name: string;
count: number;
dist: string;
}
5 字段 vs App 32 Point 5 字段:
- App 32: id/emoji/name/type/typeColor
- App 33: id/emoji/name/count/dist
"两种 5 字段"——"32 用 type/typeColor、33 用 count/dist"——"32 关注类型、33 关注数量距离"——"字段 = 业务关注点"——"模型的业务驱动"。
count: number 车辆数——"停车点可用车辆数"——"12/6/18/4"——"库存建模"——"数字字段"。
dist: string 距离字符串——"80m/210m/350m/480m"——"带单位"——"距离内联单位"——"同 App 32 dist"。

五、地图页的交互细节与可用性分析
5.1 整卡可点 vs 导航按钮
地图卡(整卡可点) vs "导航 >"(按钮可点)——"两种点击区域"——"整卡 = 打开地图、导航 = 去停车点"——"两种跳转"——"操作的细分"。
5.2 车辆数的"决策价值"
12/6/18/4 辆——"车辆数的决策"——"选车辆多的停车点"——"宿舍区 18 辆 = 首选"——"数量 = 决策因素"——"库存的透明"。
5.3 距离的"位置感"
80/210/350/480m——"距离的直观"——"80m 近、480m 远"——"距离 = 步行时间"——"用户选近的"——"距离 + 数量的权衡"——"近但少 vs 远但多"。
5.4 4 停车点的"数量分布"
12/6/18/4——"4 个数量"——"宿舍最多 18、运动场最少 4"——"数量的差异"——"按需求分配"——"demo 的真实感"。
5.5 模拟地图 vs 真实地图
浅青卡 + 🗺️——"占位地图"——"Map Kit 占位"——"真实产品集成地图 SDK"——"占位 → 真实的桥"。
5.6 📍 的"统一 emoji"
4 个停车点都用 📍——"统一的定位符号"——"📍 = 位置"——"emoji 的语义"——"统一 = 简单"。
5.7 停车点的"布局"
1 列 List——"列表式布局"——"4 停车点 1 列"——"信息完整"——"每屏 4 点"——"列表的直观"。
5.8 地图卡的高度
180 高——"比 32 的 160 高"——"更显眼"——"地图的视觉重量"——"高度 = 重要度"。
5.9 数据的"跨页一致"
40 辆车 + 4 停车点——"地图总览"——"首页 5 可用 + 6 车"——"跨页数据呼应"——"demo 的数据一致性"。
5.10 地图页的"色彩心理学"
青蓝(#2D9CDB)——"蓝色 = 地图/水域"——"地图的视觉共识"——"青蓝 = 共享单车蓝"——"地图配色的惯例"。
5.11 导航按钮的"转化价值"
"导航 >"——"一键导航"——"转化终点"——"真实产品调起地图 App"——"导航 = 行动"。
5.12 停车点的"场景覆盖"
南门/教学/宿舍/运动——"4 类场景"——"校园的 4 区域"——"场景全覆盖"——"停车点的布局逻辑"。
5.13 车辆数的"建模"
count: number——"车辆数建模"——"库存字段"——"数字直接用于显示"——"模型简单"。
5.14 地图页的"信息层级"
L1 标题(Header)→ L2 地图(MapCard)→ L3 列表(SpotList)——"3 层信息"——"从标题到视觉到详情"——"渐进式披露"。
5.16 地图页的"设计哲学"
地图页的 3 个设计哲学:
- 位置优先——停车点按距离排序
- 数量优先——车辆数清晰展示
- 导航优先——每个停车点都有导航
"位置 + 数量 + 导航的地图页"——"工具型地图页的设计原则"——"从哲学看设计"。
5.17 地图页的"对比与统一"
统一:4 停车点同结构(📍 + 名称 + 距离 + 数量 + 导航)——"列表项的统一"。 对比:4 停车点数据不同(名称/距离/数量)——"差异在数据"——"结构同构 + 数据异构"。
5.18 地图页的"终评"
地图卡 + 4 停车点 + 导航 = 完整地图页——"地图页的完成度"——"可上线级"——"系列地图型页面的标杆"。
5.20 地图页的"决策双因素"
距离(80-480m) + 数量(4-18 辆)——"双因素决策"——"近 + 多 = 最佳"——"南门 80m + 12 辆 vs 宿舍区 350m + 18 辆"——"权衡"——"近但少 vs 远但多"——"用户的心理权衡"——"双字段的决策价值"。
5.21 地图页的"导航反馈"
"导航: 南门停车点"——"导航的反馈"——"占位跳转"——"Toast 反馈"——"真实产品调起高德/百度"——"反馈的完整"。
5.22 地图页的"停车点命名"
南门/教学区/宿舍区/运动场——"4 类区域命名"——"校园的通用区域名"——"用户熟悉"——"命名的通用性"——"零学习成本"。
5.23 地图页的"列表密度"
4 停车点 + space 10——"紧凑列表"——"1 屏 4 点"——"不滚动"——"信息完整"——"列表的密度"。
5.24 地图页的"占位价值"
模拟地图的占位——"快速呈现"——"不依赖真实数据"——"demo 优先"——"占位的可替换性"——"占位 → 真实的升级路径"。
5.25 地图页的"用户场景"
"我去哪骑车?"——"用户的问题"——"地图页的答案"——"4 停车点 + 数量 + 距离"——"地图解决位置问题"——"场景驱动的设计"。
5.26 地图页的"数量语义"
"辆可用" 文案——"可用 = 可骑"——"数量 + 状态"——"12 辆可用 = 12 辆可骑"——"与首页'可用'状态呼应"——"语义的一致性"——"文案的精准"。
5.27 地图页的"颜色符号"
📍 emoji + 青蓝文字——"颜色符号体系"——"📍 = 位置、青蓝 = 品牌"——"符号的语义"——"颜色与 emoji 的组合"——"视觉的层次"。
5.28 地图页的"数据可读性"
"80m · 12 辆可用" 一行全信息——"紧凑可读"——"距离 + 数量一行"——"信息密度"——"可读性优先"——"12 号灰字的清晰"。
5.29 地图页的"demo 价值"
模拟地图的演示价值——"快速验证布局"——"不依赖 Map SDK"——"低成本演示"——"真实化路径清晰"——"占位即可用"——"demo 到产品的桥"。

六、总结:青蓝 + 模拟地图 + 4 停车点
App 33 地图页一句话:用青蓝(#2D9CDB)组织一个"地图卡 + 4 停车点"的工具地图页——180 高浅青模拟地图卡配 🗺️ 44 号 emoji + "40 辆车分布在 4 个停车点"数据总览,4 个停车点列表用 📍 emoji + 名称 + 距离 + 车辆数 + 导航按钮让用户快速定位选点。
3 个设计亮点:
- 数据化地图卡——"40 辆车分布在 4 个停车点"总览 + 🗺️ 44 号
- 车辆数透明——每停车点清晰显示"12/6/18/4 辆可用"
- 距离 + 数量双字段——"80m · 12 辆可用"一行展示决策信息
主题色设计逻辑:青蓝 = 地图/共享单车的经典蓝——"地图型页面用蓝色,因为蓝色是地图的视觉共识"——与 App 32 晴空蓝对比——"32/33 双蓝的地图"——"33 的青蓝更亮"。
与系列呼应:固定 56 高 Header(系列)、Stack 居中占位卡(32 地图/30 公益)、ForEach 列表(系列)、"导航 >"按钮(32 地图)、距离字段(32 首页)——"33 把系列的地图占位 + 列表 + 导航手法组合出'单车地图'场景"。
体验细节:数据总览、车辆数透明、距离排序、导航入口——"地图页的核心 = 让用户'3 秒看清哪里车多 + 5 秒导航'"。
可扩展性展望:Map Kit 集成、真实坐标、车辆实时数量、停车点增删、路线规划、导航联动——"demo 的单车地图页骨架完整"。
六、扩展思考:地图页的转化路径与设计哲学
6.1 地图页的"转化路径"
完整借车地图漏斗:
看地图卡(40 辆总览)→ 看停车点列表(4 点)→ 比较距离与数量 → 点导航 → 去停车点 → 扫码骑车
"地图的转化路径"——地图卡是"总览"、停车点是"详情"、导航是"行动"——"从总览到行动"——"地图页的转化链"——"地图 = 位置决策 + 行动触发"。
6.2 地图页的"信息架构"
3 层信息:
- L1 标题(Header)——"车辆地图"
- L2 总览(MapCard)——"40 辆 4 点"
- L3 详情(SpotList)——"具体停车点"
"3 层渐进式"——"从标题到总览到详情"——"信息的金字塔"——用户按需深入——"渐进式信息披露"。
6.3 地图页的"业务建模"
Spot 模型 5 字段(id/emoji/name/count/dist)——"5 字段建模"——"停车点的核心字段"——"最小可用集"——加坐标(lat/lng)、加车位(capacity)、加状态(status)即可扩展——"数据模型的扩展点"。
6.4 地图页的"对比与统一"
统一:4 停车点同结构(📍 + 名称 + 距离 + 数量 + 导航)——"列表项的统一"。 对比:4 停车点数据不同(名称/距离/数量)——"差异在数据"——"结构同构 + 数据异构"。
6.5 地图页的"设计哲学"
地图页的 3 个设计哲学:
- 位置优先——停车点按距离从近到远排序
- 数量优先——车辆数清晰可见(选多不选少)
- 行动优先——每个停车点都有导航入口
"位置 + 数量 + 行动的地图页"——"工具型地图页的设计原则"——"从哲学看设计"——"这套方法论可直接迁移到任何地图/位置型 App"。
6.6 地图页的"终评"
App 33 地图页综合评估——"三区结构 + 青蓝主题 + 完整位置信息"——"地图页的完成度评级"——"一个可上线级的单车地图页"——"系列地图型页面的标杆"——"demo 的高完成度"。
七、彩蛋:地图页的细节设计
7.1 地图卡的"数据文案"
"40 辆车分布在 4 个停车点"——"数据化文案"——"40 + 4 的数字"——"总览信息"——"比纯标题更有信息量"——"数据文案的力量"。
7.2 距离的"顺序"
80m → 210m → 350m → 480m——"距离升序"——"从近到远"——"排序逻辑"——"近的优先"——"用户看第一个就是最近的"——"排序的用户价值"。
7.3 车辆数的"分布"
12/6/18/4——"数量差异大"——"18 vs 4"——"对比明显"——"用户快速判断"——"数量的可视化"。
7.4 南门的"80m"
"80m"——"最近停车点"——"南门 = 学校入口"——"入口就近停车"——"位置与距离的匹配"——"真实校园布局"。
7.5 宿舍区的"18 辆"
"18 辆"——"最多停车点"——"宿舍 = 骑行高频"——"车辆分配逻辑"——"按需求分配"——"数据的业务合理性"。
7.6 运动场的"4 辆"
"4 辆"——"最少停车点"——"运动场 = 低频"——"按需分配"——"4 辆足够"——"数据的分层"。
7.7 📍 的"语义"
"📍 = 位置"——"定位 emoji"——"位置符号"——"emoji 的语义"——"统一图标"——"简单明了"。
7.8 导航按钮的"位置"
"导航 >" 在右侧——"右侧 = 拇指区"——"右手操作"——"导航入口的位置"——"右侧按钮的设计"。
7.9 停车点卡的"padding"
14 padding——"标准内边距"——"点击区舒适"——"卡片的呼吸"——"14 = 系列标准"。
7.10 地图卡的"Stack 层叠"
Stack 背景 + 前景——"层叠布局"——"背景浅青 + 前景居中"——"地图卡的视觉"——"Stack 的便利"。
7.11 停车点的"emoji 一致"
4 个 📍——"统一定位符号"——"不加颜色区分"——"停车点无类型差异"——"emoji 的简单"——"与 32 伞点图例对比"。
7.12 地图页的"跨页数据"
40 辆总览——"与首页 6 车 Grid 呼应"——"地图是总览、首页是详情"——"数据的层次"——"demo 的数据一致性"。
7.13 地图页的"可滚动"
Scroll 容器——"4 停车点可滚动"——"内容超出可滚"——"滚动适配"——"列表的可扩展性"。
7.14 地图页的"色彩一致性"
全页青蓝——"地图卡浅青 + 导航青蓝 + 文字青蓝"——"品牌色贯穿"——"颜色的统一"——"青色记忆"。
7.15 地图页的"demo 完整性"
地图卡 + 4 停车点 + 导航 = 完整地图页——"功能完整"——"用户可决策"——"demo 的完成度"——"从总览到行动的完整链"。
八、总结与展望
App 33 地图页的核心价值:用青蓝 + 模拟地图 + 4 停车点,构建一个"3 秒看清哪里车多 + 5 秒导航"的工具地图页——这是共享单车 App 地图页的标准范式。
与系列对比:
| App | 地图/位置类页面 | 决策 |
|---|---|---|
| 27 | 日历 | 何时 |
| 28 | 衣橱 | 怎么穿 |
| 29 | 餐厅 | 吃什么 |
| 31 | 菜单 | 喝什么 |
| 32 | 伞点地图 | 去哪借伞 |
| 33 | 单车地图 | 去哪骑车 |
"33 地图页的决策是'去哪骑车'"——"地图页 = 位置决策辅助"——"每个地图类页面都在帮用户做位置决策"——"33 帮用户选停车点"。
终评:青蓝 + 模拟地图 + 4 停车点 + 导航 = 完整工具地图页——"从配色到信息架构的完整设计"——"地图页的标杆"。
补充说明:本篇所有代码引用均基于 33-campus-bike/entry/src/main/ets/pages/Func1Tab.ets 实际源码,行号与源码一一对应;4 张实机截图分别覆盖地图首屏、地图卡点击、南门停车点导航、宿舍区停车点导航四种差异场景,全部含模拟器轮廓,图床域名统一为 imgbed.devzoo.top。

更多推荐





所有评论(0)