
第19篇沉浸式首页地图、玻璃层、信息卡片的层级关系第 5 天开始看「双镜记忆相机」的首页体验。这个首页不是把地图放满屏这么简单它同时承担了四件事地图可操作、顶部信息可读、中间浮动控件不挡手、底部记忆入口和导航不遮住地图。本文会按“运行环境、视觉层级、源码验证、失效风险”四步拆开。先看页面效果再回到Index.ets对照MapComponent、getMapBottomOverlayInset()和HitTestMode这样读者能知道每一层为什么放在当前位置。配图说明这张图用于观察地图可拖动区域、附近景点按钮、最近记忆 Dock 和底部导航之间的遮挡关系。官方依据官方 MapKit 文档《MapComponent地图组件》说明MapComponent需要传入mapOptions并通过回调获取MapComponentController。这决定了首页第一层必须是稳定的地图容器其他 UI 只是覆盖层不能反过来破坏地图手势。本文讨论的是 HarmonyOS Stage 模型下的 MapKit 首页不是 Web 地图也不是静态背景图。判断页面质量时不能只看“像不像沉浸式”还要看地图手势、覆盖层触摸和底部安全区域是否同时成立。开发环境版本号与失效风险提示先把本文的可复现边界放在前面避免只看截图就迁移到不一致的工程里。项目本文使用的版本或配置验证依据DevEco Studio6.1README.md的开发环境说明HarmonyOS SDK6.1.0(23)README.md与build-profile.json5默认产品targetSdkVersion 6.1.0(23)compatibleSdkVersion 6.1.0(23)build-profile.json5模拟器产品targetSdkVersion 6.0.2(22)compatibleSdkVersion 6.0.2(22)build-profile.json5工程模型Stage 模型apiType: stageModeentry/build-profile.json5设备类型phone、tablet、2in1entry/src/main/module.json5地图能力com.huawei.service.mapkit名称为Map Kitbuild-profile.json5失效风险也要写清楚缺少 MapKit 能力、定位权限被拒绝、网络不可用、窗口尺寸在手机/平板/2in1 之间切换都会影响首页表现。后文会分别给出配置检查、触摸命中检查和降级策略。首页四层结构沉浸式首页最容易乱的地方是把“视觉层级”和“触摸层级”混在一起。这个页面的正确拆法是地图底图层先保证可交互顶部信息层只接管自己的卡片区域浮动控件层只接管按钮底部入口层通过 inset 处理遮挡。配图说明四层结构对应MapComponent、顶部信息、浮动控件、底部 Dock 和导航。重点不是叠得多而是每一层只拿自己该拿的触摸。主要文件是entry/src/main/ets/pages/Index.etsmapOptions地图初始经纬度、缩放级别、手势开关。buildMapTab()首页地图和覆盖层的组合入口。getMapBottomOverlayInset()为底部 Dock 和导航预留地图区域。buildActiveTabContent()地图页叠加景点助手、详情弹层等更高层内容。核心结构可以简化成这样Stack({ alignContent: Alignment.TopStart }) { Column() { MapComponent({ mapOptions: this.mapOptions, mapCallback: this.mapCallback }) .width(100%) .layoutWeight(1) if (this.getMapBottomOverlayInset() 0) { Column() .height(this.getMapBottomOverlayInset()) .backgroundColor($r(app.color.ml_background)) } } // 顶部信息层周年回忆、错误提示、详情卡片 // 中间控件层定位、地图操作 // 底部入口层最近记忆 Dock 底部导航 }这里最关键的不是Stack而是每一层的职责边界。地图层负责可交互底图顶部Scroll只在有内容时出现并设置hitTestBehavior(HitTestMode.Block)浮动控件和底部容器使用HitTestMode.Transparent把空白区域的触摸还给地图。源码定位为了避免读者只看文字猜实现下面这张图给出源码搜索入口。打开项目后优先搜索mapOptions、buildMapTab和getMapBottomOverlayInset基本就能定位首页地图的主线。配图说明源码截图用于确认本文不是凭截图推断而是直接对应Index.ets中的状态、函数和资源引用。mapOptions不是装饰配置它决定地图是否真能承担首页底图的角色。项目里保留了滚动、缩放、旋转、倾斜四类手势同时关闭系统默认定位控件把定位入口交给自定义浮层private mapOptions: mapCommon.MapOptions { position: { target: { latitude: 30.25113, longitude: 120.15515 }, zoom: 12.6 }, dayNightMode: mapCommon.DayNightMode.AUTO, scrollGesturesEnabled: true, zoomGesturesEnabled: true, rotateGesturesEnabled: true, tiltGesturesEnabled: true, scaleControlsEnabled: false, myLocationControlsEnabled: false, logoScale: 0.9 };这段代码和官方MapComponent文档是对应的地图组件通过mapOptions获得初始位置、缩放和交互能力通过mapCallback拿到控制器。如果后续为了避免误触把手势关掉首页就只剩一张“像地图的背景图”产品价值会下降。底部 inset 为什么必须单独算getMapBottomOverlayInset()的逻辑很直白private getMapBottomOverlayInset(): number { if (this.shouldUseSideNavigation()) { return 0; } const baseInset this.getMapPhotoDockMemories().length 0 ? 272 : 128; return baseInset this.getSafeAreaBottomVp(); }手机形态下底部同时可能出现「最近记忆」和四个 Tab。如果地图仍然铺到底部用户会看到 Marker、定位按钮和导航挤在一起。这里用272/128 safeArea明确预留空间有最近记忆时留更多没有时只给导航留底。切到侧边导航后返回 0因为底部已经不承担主导航。信息卡片和地图如何共存地图页不只有地图还会出现附近景点、智能助手、记忆详情等更高层内容。它们可以覆盖在地图上但不能把整张地图变成不可拖拽的背景。配图说明这张图展示了信息卡片覆盖地图后的阅读形态。卡片区域需要接管滚动和点击卡片之外的地图区域仍然应该可拖拽。buildMapTab()里有三种命中策略Scroll() { // 周年回忆、错误提示、详情卡片 } .hitTestBehavior(HitTestMode.Block) Column() { Blank() this.buildMapFloatingControls() Blank() } .hitTestBehavior(HitTestMode.Transparent) Column({ space: 14 }) { if (this.getMapPhotoDockMemories().length 0) { this.buildMapPhotoGroupDock() } this.buildBottomNavigation() } .hitTestBehavior(HitTestMode.Transparent)顶部信息层使用Block因为卡片内部需要滚动和点击不能把事件透传给地图。浮动控件和底部容器使用Transparent因为只有按钮或卡片本身应该响应空白区域要还给地图。复现矩阵场景预期结果对应代码手机竖屏无最近记忆地图底部只预留导航高度baseInset 128手机竖屏有最近记忆最近记忆 Dock 和导航都不遮地图baseInset 272平板或 2in1 宽窗口地图不再追加底部 insetshouldUseSideNavigation()返回 true详情卡片打开卡片滚动不拖动地图HitTestMode.Block底部空白区域拖动地图仍响应拖拽HitTestMode.Transparent未授权定位顶部出现可解释错误不影响地图底图mapErrorText信息层无 MapKit 能力或地图初始化失败显示降级内容不能让页面空白mapCallback/错误状态如果要把这篇文章里的实现迁到自己的应用建议先跑这张表而不是先改视觉。只要这些场景稳定后续换图标、换玻璃色、改卡片圆角都不容易伤到地图交互。自测清单这篇对应的自测很适合做成真机检查单拒绝定位权限确认mapErrorText出现在顶部信息层。选择一个记忆点确认showDetailPanel打开后卡片可以滚动。有最近记忆和无最近记忆两种状态下分别观察底部留白。横屏或平板宽度触发侧边导航后确认getMapBottomOverlayInset()为 0。暂时关闭网络或 MapKit 能力时确认页面有降级内容不出现空白页。本地还可以用下面这条命令核对版本和能力rg -n targetSdkVersion|compatibleSdkVersion|apiType|deviceTypes|com.huawei.service.mapkit build-profile.json5 entry/build-profile.json5 entry/src/main/module.json5当前核对结果包含targetSdkVersion: 6.1.0(23)、compatibleSdkVersion: 6.1.0(23)、apiType: stageMode、deviceTypes: phone/tablet/2in1和capability: com.huawei.service.mapkit。这就是本文后面讨论 MapKit 首页层级、触摸命中和底部占位的前提。小结沉浸式首页的质量不在于把组件都堆到一张图上而在于层级之间互不抢职责。地图是第一层信息卡是状态投影浮动控件是快捷操作底部导航是路径入口。只要这四层的触摸、占位和显隐规则清楚页面就能从手机扩展到平板和 2in1而不是每个设备重新写一套首页。参考依据华为开发者文档《MapComponent地图组件》项目源码entry/src/main/ets/pages/Index.ets