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

第21篇|侧边导航:平板和 2in1 为什么不照搬手机布局

第21篇|侧边导航:平板和 2in1 为什么不照搬手机布局

手机上的底部导航很自然,因为拇指容易触达;但平板和 2in1 的窗口更宽,底部导航会把页面压得很低,也会让地图、相册、保险箱这些内容区域失去横向优势。所以这篇不讲“把导航放左边更高级”,而是看代码里如何用窗口尺寸决定导航形态。

官方多设备最佳实践《响应式布局》《自适应布局》强调,界面需要随窗口大小、设备形态等变化调整布局。项目没有靠设备名硬切,而是用窗口宽高做第一判断。

断点在哪里

关键函数只有两行:

private shouldUseSideNavigation(): boolean { return this.getWindowWidthVp() >= 600 && this.getWindowHeightVp() >= 360; }

也就是说,只要窗口宽度达到 600vp,且高度不低于 360vp,就切换到侧边导航。这个条件比“平板才用侧边栏”稳,因为 2in1、PC 窗口化、平板分屏都可能改变真实可用区域。

侧栏宽度也不是固定值:

private getSideNavigationWidth(): number { return this.getWindowWidthVp() >= 840 ? 118 : 96; }

600vp 到 839vp 使用 96vp,840vp 以上使用 118vp。这样中等宽度窗口不会被导航吃掉太多空间,大屏窗口又能给文字和图标更舒服的点击区域。

根布局如何切换

buildAdaptiveRoot()把两种布局分开:

if (this.shouldUseSideNavigation()) { Row() { this.buildSideNavigation() Stack() { this.buildActiveTabContent() } .layoutWeight(1) } } else { this.buildActiveTabContent() }

手机形态直接渲染内容页,导航由内容页底部覆盖层负责;大屏形态先渲染侧边栏,再把当前内容放进右侧Stack。这比在每个页面里分别判断“要不要左边距”更干净,主布局入口只有一个。

地图页为什么要取消底部占位

第 19 篇讲过getMapBottomOverlayInset(),在侧边导航下它直接返回 0:

if (this.shouldUseSideNavigation()) { return 0; }

原因很实际:导航已经移到左侧,底部没有必要再为空导航保留 128/272vp。如果忘了这一步,大屏地图会莫名少一截,用户会以为底部还有内容没展开。

侧边导航的视觉和交互

buildSideNavigation()仍然复用buildNavIconMark(tab),因此图标资源和底部导航一致;差异只在容器方向、宽度、点击区域和安全区处理:

.width(this.getSideNavigationWidth()) .height('100%') .backgroundColor($r('app.color.ml_nav_glass')) .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])

这里的expandSafeArea让侧边栏贴合系统区域,避免大屏或沉浸式窗口下顶部/底部出现断层。选中态仍然通过ml_selected_glass、边框、阴影和hdsEffect表达,不需要重新设计另一套视觉语言。

自测建议

  • 手机竖屏:底部导航出现,侧边栏不出现。
  • 宽度约 600vp:侧边栏出现,底部导航消失。
  • 宽度约 840vp:侧边栏从 96vp 切到 118vp,文字不截断。
  • 地图页大屏:底部不再保留导航占位。
  • 2in1/PC:shouldShowHomeXiaoYiOverlay()会避开桌面类设备,避免首页叠层过多。

为什么用窗口断点,不用设备名

官方多设备文档反复强调“窗口大小”和“可用显示区域”。这和传统手机应用的设备适配思路不一样。项目里虽然也能拿到deviceInfo.deviceType,但侧边导航没有直接写成:

return deviceInfo.deviceType === 'tablet';

原因有三个。第一,2in1 在平板和桌面之间切换,设备名不能表达当前窗口。第二,平板分屏后可用宽度可能小于普通手机横屏。第三,PC 窗口可自由缩放,如果只看设备名,窄窗口也会被强行塞进侧边栏。

项目的真实策略是:

return this.getWindowWidthVp() >= 600 && this.getWindowHeightVp() >= 360;

这句代码把问题从“我是什么设备”改成“我现在有多少空间”,更符合响应式布局的判断依据。

断点矩阵

宽度和高度导航形态侧栏宽度地图底部 inset
宽度小于 600vp底部导航128/272 + safeArea
宽度大于等于 600vp,高度小于 360vp底部导航128/272 + safeArea
600vp 到 839vp,且高度足够侧边导航96vp0
840vp 以上,且高度足够侧边导航118vp0

这张表可以直接作为验收依据。尤其要注意高度条件:有些横向窗口很宽但很矮,如果强行放侧边栏,内容区会变得像一条横幅,操作反而困难。

代码验证 1:侧边栏是主布局的一部分

侧边导航不在某个页面内部补左边距,而是在根布局中占位:

Row() { this.buildSideNavigation() Stack() { this.buildActiveTabContent() } .layoutWeight(1) .height('100%') } .backgroundColor($r('app.color.album_background'))

这说明侧边栏和内容区是兄弟节点。好处是每个 Tab 不需要各自记住“左边有一个导航栏”,相机、地图、相册和保险箱都只关心自己的内容。主布局负责空间分配,子页面负责业务状态,这样代码边界更清楚。

代码验证 2:侧栏也使用安全区

侧边栏容器最后调用:

.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])

这个细节容易漏。大屏、沉浸式窗口、折叠屏和 2in1 模式下,顶部和底部系统区域不一定和手机完全一致。侧栏扩展到系统安全区,能避免上方出现一条突兀空隙,也能让导航背景和整个窗口边界对齐。

常见错误

第一种错误是“只移动导航,不移动空间责任”。比如把底部导航改成左侧绝对定位,但内容区仍然按全屏宽度计算,最后侧栏盖住地图或列表。第二种错误是“侧栏宽度固定”,在 600vp 附近占掉太多内容,在 1000vp 以上又显得局促。第三种错误是“底部导航没有退出”,大屏同时存在两套入口,用户不知道哪个才是主路径。

本文的实现分别用Row根布局、getSideNavigationWidth()buildBottomNavigation()的早返回解决这三个问题。

复现步骤

  1. 在预览器中把窗口宽度调到 599vp,确认底部导航还在。
  2. 调到 600vp,确认侧边栏出现,底部导航消失。
  3. 调到 840vp,确认侧边栏加宽,文字仍然是一行。
  4. 进入地图页,确认底部没有多余空白。
  5. 切换相册或保险箱,确认右侧内容区自动占满剩余宽度。

这组步骤能覆盖窗口断点、导航互斥、内容占位和安全区四类风险,比只截一张大屏图更有说服力。

小结

一多适配不是把手机界面等比放大,而是把“路径入口”和“内容空间”重新分配。这个项目的侧边导航实现有三个好处:断点基于窗口而非设备标签,根布局统一切换,导航资源和选中态复用手机实现。这样改动面积小,但用户在平板和 2in1 上拿到的是另一种合适的布局,而不是被拉宽的手机页。

参考依据:

  • 华为开发者最佳实践《响应式布局》:https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-multi-device-responsive-layout
  • 华为开发者最佳实践《自适应布局》:https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-multi-device-adaptive-layout
  • 项目源码:entry/src/main/ets/pages/Index.ets
http://www.rkmt.cn/news/1424342.html

相关文章:

  • 【原创解锁】15日天气预报 解锁会员 精准预警超好用
  • C++跨平台开发:微信聊天记录导出工具架构解析与实现
  • 挖坑指南:为什么你的数据采集卡老是“丢帧”?一篇文章讲透Flash、FRAM、PSRAM的区别与实战
  • 三步轻松复活经典游戏联机:IPXWrapper让老游戏重获新生
  • 别再瞎测了!用IxChariot给工业网关做吞吐量测试,这5个坑我帮你踩过了
  • Photoshop AVIF插件深度探索:为什么这款开源神器正在改变图像处理工作流?
  • 别再重装系统了!LightDM报错‘Failed to Start’的5种修复方案与深度解析
  • Flutter Hero Animation 详解
  • 2026年Q2北京铝合金回收:北京溴化锂机组回收/北京电器回收/北京电子设备回收/北京电池回收/北京电线电缆回收/选择指南 - 优质品牌商家
  • 【图像提取】基于数学形态学的数字视网膜图像血管提取 (DRIVE) 数据集分割附Matlab代码
  • 【AI搜索革命性差异指南】:3大核心维度拆解AI搜索与传统搜索的底层逻辑差异
  • 【绿化】Fong投屏 一键手机投屏 多设备兼容超稳定
  • 深入Windows消息循环:手把手教你用Unity拦截WM_SIZING实现自定义窗口控制
  • 如何选择工程信息平台?2026年5月推荐口碑好的服务项目人脉难寻痛点 - 品牌推荐
  • 5分钟终结VC运行库安装难题:一站式解决方案深度解析
  • Lindy内容创作自动化:从零搭建抗衰减内容引擎的4层架构,含GitHub开源模板
  • Linux系统终极解决方案:Dislocker轻松访问BitLocker加密分区
  • AMBA 总线接口访问明细
  • Agent赋能下药物警戒自动生成的个例报告符合监管要求吗?深度拆解AI Agent在PV领域的合规边界
  • 178、运动控制中的行业标准:功能安全IEC 61508
  • 技术人的个人理财:从入门到精通
  • 微信聊天记录永久保存完整指南:WeChatExporter开源工具使用教程
  • 从零开发游戏需要学习的c#模块,第三十一章(技能冷却系统 —— 范围爆炸)
  • DroidCam OBS插件终极指南:让手机摄像头快速变身高清直播源
  • 3个核心功能彻底解决Windows C盘爆红问题:开源工具Windows Cleaner深度解析
  • 微信视频号直播数据抓取终极指南:5分钟搭建专业级监控系统
  • Prompt Engineering 深度解析:从 Few-shot 到结构化提示的系统化方法
  • 基于STM32的多功能万年历电子闹钟设计与实现
  • 从“事后Debug”到“事前防御”:聊聊C#代码契约(Code Contracts)与Assert断言的配合使用
  • 2025-2026年全球留香沐浴露品牌推荐:十大口碑产品评测约会前提升魅力价格注意事项 - 品牌推荐