当前位置: 首页 > news >正文

第20篇|底部导航:地图、拍照、相册、保险箱的产品路径

第20篇|底部导航:地图、拍照、相册、保险箱的产品路径

底部导航看起来只是四个入口,但在一个相机类应用里,它实际定义了产品路径:从「在哪里拍」进入地图,从「马上拍」进入相机,从「拍完看」进入相册,从「私密内容」进入保险箱。导航如果只做成按钮,就会漏掉状态、权限和内容边界。

本篇对应源码仍然是entry/src/main/ets/pages/Index.ets,重点看buildBottomNavigation()buildNavItem()buildNavIconMark()

四个入口不是四个普通按钮

项目里的底部导航是这样组织的:

Row() { this.buildNavItem('地图', 'map') this.buildNavItem('拍照', 'camera') this.buildNavItem('相册', 'gallery') this.buildNavItem('保险箱', 'vault') }

每个入口都走同一个buildNavItem(label, tab),这样选中态、字体、边框、光效、点击行为可以统一维护。统一并不等于行为完全一样,gallery的点击会走openFullGalleryFromNavigation(),其他入口走switchTab(tab)。这是一个很小但重要的产品判断:相册不是普通页签,它要进入完整浏览路径。

为什么导航层要透明命中

地图页底部覆盖层代码里有一段容易被忽略:

Column({ space: 14 }) { if (this.getMapPhotoDockMemories().length > 0) { this.buildMapPhotoGroupDock() } this.buildBottomNavigation() } .hitTestBehavior(HitTestMode.Transparent)

它的意思是:容器空白区域不吃掉手势,真正的卡片和按钮才响应点击。这样用户在底部附近仍然能拖动地图,不会感觉地图「死掉了一块」。这比单纯调zIndex更可靠,因为问题不在层级,而在命中区域。

选中态为什么要同时改图标、边框和光效

buildNavIconMark()使用资源命名来区分默认态和选中态:

Image(this.isActiveTab(tab) ? $r('app.media.nav_camera_active') : $r('app.media.nav_camera'))

再配合ml_selected_glassgetNavGlowColor(tab)hdsEffect.HdsEffectBuilder().pointLight(...),选中态会同时体现在图标、背景、边框和光效上。官方 UIDesignKit 文档提供了hdsEffectHdsEffectBuilder,项目里用它做导航玻璃层的光照反馈,而不是只把文字变粗。

和侧边导航的关系

buildBottomNavigation()的第一句是:

if (this.shouldUseSideNavigation()) { Blank().width(0).height(0) } else { // 构建底部导航 }

这说明底部导航只服务手机形态。宽度达到平板/2in1 条件后,主入口交给侧边导航。这个判断避免了一个常见问题:大屏上同时出现底部导航和侧边导航,路径重复,空间也浪费。

自测建议

  1. 在地图页拖动底图,确认底部透明区域不阻断地图手势。
  2. 依次点击地图、拍照、相册、保险箱,确认选中态和页面内容一致。
  3. 点击相册入口,确认走完整相册路径,而不是只切普通 Tab。
  4. 把窗口宽度调到 600vp 以上,确认底部导航消失、侧边导航出现。
  5. 切深色模式,确认ml_nav_glassml_selected_glass、文字色和图标仍然可读。

导航行为不是一个 switch 就结束

底部导航的点击分支看起来很短,但背后有清楚的产品语义:

.onClick(() => { if (tab === 'gallery') { this.openFullGalleryFromNavigation(); return; } this.switchTab(tab); })

mapcameravault是主 Tab 切换,gallery是完整浏览路径。这样设计是因为相册页不仅展示缩略图,还承担大图查看、筛选、回看和返回逻辑。如果相册也只做switchTab('gallery'),用户从首页进入后会丢失“完整浏览”的上下文,后续文章讲相册详情时就会发现入口不一致。

视觉状态的四个来源

一个导航项的选中态由四部分组成,不是单独靠颜色:

来源代码位置作用
图标资源buildNavIconMark(tab)默认态和_active成对切换
玻璃背景ml_selected_glass让当前入口有稳定承托面
边框和阴影getNavGlowColor(tab)区分不同业务入口的强调色
光照效果hdsEffect.HdsEffectBuilder()让玻璃层有系统级质感

官方hdsEffect文档提供的是效果能力,项目里没有把它当成装饰堆叠,而是只在导航容器和选中项上使用。这样做的好处是光效有明确语义:它只强调“你现在在哪个路径里”。

代码验证 1:底部导航只在手机形态出现

buildBottomNavigation()开头直接判断侧边导航:

if (this.shouldUseSideNavigation()) { Blank() .width(0) .height(0) } else { // 构建底部导航 }

这段代码可以用窗口宽度验证。把预览器或真机窗口拖到 600vp 以上,底部导航应该消失;再缩回手机宽度,它应该重新出现。这个判断比在 CSS 或样式里隐藏某个容器更可靠,因为它从构建阶段就不再输出底部导航,避免大屏上出现两个主入口。

代码验证 2:资源成对存在

第 20 篇用到的导航资源必须成对存在,否则选中态会断。可以在项目根目录运行:

Get-ChildItem entry/src/main/resources/base/media -File | Where-Object { $_.Name -like 'nav_*' } | Sort-Object Name

应该能看到nav_map.pngnav_map_active.pngnav_camera.pngnav_camera_active.pngnav_gallery.pngnav_gallery_active.pngnav_vault.pngnav_vault_active.png。这就是官方资源访问文档里“通过资源文件管理媒体资源”的落地方式:代码只写$r('app.media.nav_camera'),真正替换视觉时改资源文件,不改导航逻辑。

可复用检查清单

如果你也在做相机、地图或工具类应用,可以按下面顺序检查底部导航:

  1. 先确认每个入口是否代表不同任务,而不是仅仅分页面。
  2. 再确认特殊路径是否单独处理,例如本文的相册入口。
  3. 然后检查命中区域,容器空白不要吞掉地图或内容手势。
  4. 最后检查大屏降级,底部导航和侧边导航不要同时存在。

这套顺序比先画 UI 更稳,因为它先确定“入口要完成什么任务”,再决定“入口长什么样”。

小结

一个高质量导航,不只是能点。它要表达当前所在位置,要把不同业务路径分开,还要在地图这种强交互背景上处理好命中区域。这里的实现把「路径统一」和「行为差异」放在同一层:UI 统一由buildNavItem()承担,特殊路径由点击分支承担,设备差异由shouldUseSideNavigation()承担。

参考依据:

  • 华为开发者文档《hdsEffect》:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ui-design-hdseffect
  • 华为开发者文档《资源分类与访问》:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/resource-categories-and-access
  • 项目源码:entry/src/main/ets/pages/Index.ets
http://www.jsqmd.com/news/913043/

相关文章:

  • 3步快速导出QQ空间完整历史记录:GetQzonehistory终极指南
  • 3步部署WenQuanYi Micro Hei:解锁高效中文显示的轻量级解决方案
  • STM32 HAL库实战:用TB6612FNG模块让GB37-520电机实现前进、后退、转向的多种运动模式
  • 2026年Q2不锈钢景墙厂家评测:不锈钢幕墙、不锈钢装饰线条、北京铝板廊架、园林景观廊架、幕墙铝板、玻璃栏杆、车库玻璃雨棚选择指南 - 优质品牌商家
  • 博客迁移通知
  • 赛普拉斯代理现货库存CYUSB3014-BZXC高性能USB 3.0外设控制器芯片
  • 2026年漂染水处理药剂权威供应商排行盘点:福建,泉州,闽南,日化化工原料、消泡剂水处理药剂、漂染化工原料、环保化工原料选择指南 - 优质品牌商家
  • PEExplorerV2深度解析:如何用三窗格架构解密Windows可执行文件内部秘密?
  • 终极指南:在Linux系统下无缝访问BitLocker加密分区的完整方案
  • 【原创解锁】15日天气预报 解锁会员 精准预警超好用
  • 字符串之Hash
  • 第21篇|侧边导航:平板和 2in1 为什么不照搬手机布局
  • 保姆级教程:用Matlab/Simulink+CarSim复现平行泊车仿真(附模型文件与避坑点)
  • CSS Transitions 过渡效果详解
  • 抖音音频提取革命:3分钟搞定批量下载的开源神器
  • Claude生成代码质量究竟如何?37项实测指标揭穿90%开发者忽略的隐藏风险
  • C++跨平台开发:微信聊天记录导出工具架构解析与实现
  • 【雷达干扰】FMCW 雷达稀疏低秩 Hankel 矩阵分解的干扰抑制附Matlab代码
  • 2026年近期,如何选择行业知名的液压马达定制厂家? - 2026年企业资讯
  • 挖坑指南:为什么你的数据采集卡老是“丢帧”?一篇文章讲透Flash、FRAM、PSRAM的区别与实战
  • 三步轻松复活经典游戏联机:IPXWrapper让老游戏重获新生
  • 别再瞎测了!用IxChariot给工业网关做吞吐量测试,这5个坑我帮你踩过了
  • 隐形冠军舜展智能:16年磨一剑,用等离子技术点亮中国高端制造
  • 第19篇|沉浸式首页:地图、玻璃层、信息卡片的层级关系
  • 制造业AI智能体选型:跨系统执行、任务拆解与信创适配三大技术维度对比
  • Photoshop AVIF插件深度探索:为什么这款开源神器正在改变图像处理工作流?
  • 从Windows转战Ubuntu?手把手教你无缝迁移Beyond Compare使用习惯(含dpkg安装与破解详解)
  • 16位ADC不够用?别急着换芯片!教你用“过采样+滑动平均”榨出24位极致精度
  • 别再重装系统了!LightDM报错‘Failed to Start’的5种修复方案与深度解析
  • Flutter Hero Animation 详解