
第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 使用 96vp840vp 以上使用 118vp。这样中等宽度窗口不会被导航吃掉太多空间大屏窗口又能给文字和图标更舒服的点击区域。根布局如何切换buildAdaptiveRoot()把两种布局分开if (this.shouldUseSideNavigation()) { Row() { this.buildSideNavigation() Stack() { this.buildActiveTabContent() } .layoutWeight(1) } } else { this.buildActiveTabContent() }手机形态直接渲染内容页导航由内容页底部覆盖层负责大屏形态先渲染侧边栏再把当前内容放进右侧Stack。这比在每个页面里分别判断“要不要左边距”更干净主布局入口只有一个。地图页为什么要取消底部占位第 19 篇讲过getMapBottomOverlayInset()在侧边导航下它直接返回 0if (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/PCshouldShowHomeXiaoYiOverlay()会避开桌面类设备避免首页叠层过多。为什么用窗口断点不用设备名官方多设备文档反复强调“窗口大小”和“可用显示区域”。这和传统手机应用的设备适配思路不一样。项目里虽然也能拿到deviceInfo.deviceType但侧边导航没有直接写成return deviceInfo.deviceType tablet;原因有三个。第一2in1 在平板和桌面之间切换设备名不能表达当前窗口。第二平板分屏后可用宽度可能小于普通手机横屏。第三PC 窗口可自由缩放如果只看设备名窄窗口也会被强行塞进侧边栏。项目的真实策略是return this.getWindowWidthVp() 600 this.getWindowHeightVp() 360;这句代码把问题从“我是什么设备”改成“我现在有多少空间”更符合响应式布局的判断依据。断点矩阵宽度和高度导航形态侧栏宽度地图底部 inset宽度小于 600vp底部导航无128/272 safeArea宽度大于等于 600vp高度小于 360vp底部导航无128/272 safeArea600vp 到 839vp且高度足够侧边导航96vp0840vp 以上且高度足够侧边导航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()的早返回解决这三个问题。复现步骤在预览器中把窗口宽度调到 599vp确认底部导航还在。调到 600vp确认侧边栏出现底部导航消失。调到 840vp确认侧边栏加宽文字仍然是一行。进入地图页确认底部没有多余空白。切换相册或保险箱确认右侧内容区自动占满剩余宽度。这组步骤能覆盖窗口断点、导航互斥、内容占位和安全区四类风险比只截一张大屏图更有说服力。小结一多适配不是把手机界面等比放大而是把“路径入口”和“内容空间”重新分配。这个项目的侧边导航实现有三个好处断点基于窗口而非设备标签根布局统一切换导航资源和选中态复用手机实现。这样改动面积小但用户在平板和 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