鸿蒙HarmonyOS 6横竖屏适配:从窗口旋转到布局自适应 2. 适配方案的顶层设计3. 实操搭建从创建工程到跑通横竖屏首先在 DevEco Studio 里新建一个 Empty Ability 工程选择 Stage 模型。新建工程时会默认生成 entry 模块后续代码都写在 entry 里。这个工程结构是后续所有适配工作的基础创建时记得选 HarmonyOS 6 SDK如果本地已经装了 IDE 和 SDK直接 Next 就行。创建完工程以后先别急着写代码把src/main/module.json5打开确认一下 abilities 配置。Stage 模型下每个 UIAbility 都会在 module.json5 里声明横竖屏相关的支持和接下来的配置都在这里改。3.1 页面级横竖屏配置在 HarmonyOS 的 Stage 模型里每个页面Ability的窗口方向可以单独控制。module.json5 中 abilities 数组里每个能力项支持配置orientation字段取值包括portrait竖屏、landscape横屏、auto跟随系统、auto_rotation自动旋转等。{ module: { abilities: [ { name: EntryAbility, srcEntry: ./ets/entryability/EntryAbility.ets, orientation: auto_rotation, exported: true } ] } }这个字段配置的是 Ability 的默认方向。设为auto_rotation以后设备旋转时窗口方向会跟着传感器走这就是传统意义上最接近系统默认行为的设置。如果业务里某个页面必须横屏显示比如视频播放页直接把该页面的orientation设为landscape就行系统会强制横屏。但这里有一个容易踩的坑这个字段配的是 Ability 的启动方向和默认方向不意味着运行时不能动态改。页面跑起来以后你依然可以在代码里通过窗口 API 动态切换方向后面 3.4 小节会细说。3.2 全局旋转开关与设备方向如果要让整个应用都支持横竖屏光在 module.json5 里配还不行还需要在 EntryAbility 的onWindowStageCreate里做全局设定。HarmonyOS 6 里提供了windowStage.getMainWindowSync()拿主窗口然后调用setPreferredOrientation接口来指定方向偏好。import { window } from kit.ArkUI; import { AbilityConstant, UIAbility, Want } from kit.AbilityKit; export default class EntryAbility extends UIAbility { onWindowStageCreate(windowStage: window.WindowStage): void { // 获取主窗口并设置方向autoRotation 表示跟随系统旋转 const mainWindow windowStage.getMainWindowSync(); mainWindow.setPreferredOrientation(window.Orientation.AUTO_ROTATION); } }这段代码是全局旋转支持的关键。setPreferredOrientation是一个异步接口但通过getMainWindowSync同步拿到窗口实例后调用即可。这里的window.Orientation.AUTO_ROTATION和配置文件里的auto_rotation是同一个概念只是一个是声明式一个是命令式。实际项目里我一般会把这段全局旋转逻辑单独抽成一个工具函数因为后面动态切换方向、监听旋转、保存状态都围绕窗口方向同一套 API 展开。统一封装的好处是后续如果华为在 API 版本上做了变动只改工具类一处即可不用满屏粘贴复制。3.3 页面跳转时的方向同步这里说到页面跳转很多开发者容易忽略一个问题如果 A 页面是竖屏B 页面是横屏A 跳 B 方向变化B 返回 A 方向也要变回来。这个看似简单的需求实则涉及跳转前保存方向 返回时恢复方向的完整链路。先看一个直接但有瑕疵的写法// 从 A 页面跳转 B 页面 import { router } from kit.ArkUI; router.pushUrl({ url: pages/SecondPage, }).then(() { // 跳转后立刻设置 B 页面为横屏 window.getLastWindow(getContext(this)).then((win) { win.setPreferredOrientation(window.Orientation.LANDSCAPE); }); });这个写法能跑但问题很多第一getLastWindow拿到的不一定是 B 页面的窗口时序上可能拿到 A 的残留窗口第二B 页面返回 A 页面时没有恢复方向A 页面会以横屏状态展示界面布局瞬间错乱。我在第一版实现里就是这种写法调试时发现点返回后首页布局全乱了排查了很久才定位到方向没有恢复的问题。推荐的做法是目标页在onPageShow或aboutToAppear里主动设置方向离开时再恢复。用 Navigation 或 Router 都可以关键是设置方向动作要绑定页面自己的生命周期而不是放在跳转动作的回调里。下面给出一个我实测稳定的 Router 方案。// 进入 SecondPage 时设置方向 import { window } from kit.ArkUI; Entry Component struct SecondPage { aboutToAppear(): void { const win window.getLastWindow(getContext(this)); win.then((windowObj) { windowObj.setPreferredOrientation(window.Orientation.LANDSCAPE); }); } aboutToDisappear(): void { const win window.getLastWindow(getContext(this)); win.then((windowObj) { windowObj.setPreferredOrientation(window.Orientation.PORTRAIT); }); } }这个方案为什么稳核心原因在于aboutToAppear和aboutToDisappear一定会在页面创建和销毁时触发生命周期和页面完全绑定。跳转动作只负责导航不掺合方向逻辑职责单一排错方便。如果项目用的是 Navigation推荐思路一样在NavDestination的onAppear/onDisappear回调里设置方向即可。3.4 运行时动态切换的三种姿势除了页面级的声明式配置和生命周期绑定HarmonyOS 6 还支持运行时动态切换方向。实践中我常用到三种方式分别适配不同场景。方式一通过窗口 API 直接切换async function switchOrientation(orientation: window.Orientation) { const win await window.getLastWindow(getContext(this)); await win.setPreferredOrientation(orientation); }这个 API 是全局的站在窗口层面设置方向适合一键全屏横屏一键恢复竖屏这种全局控制场景。方式二通过 Ability 的 onConfigurationUpdated 监听旋转设备主动旋转时系统会触发配置变更回调import { ConfigurationConstant } from kit.AbilityKit; onConfigurationUpdated(newConfig: Configuration): void { if (newConfig.direction ConfigurationConstant.Direction.DIRECTION_HORIZONTAL) { // 设备转为横屏 } else if (newConfig.direction ConfigurationConstant.Direction.DIRECTION_VERTICAL) { // 设备转为竖屏 } }这种方式适合监听用户手动翻转设备的行为很多游戏和视频类 App 会在旋转发生时调整 UI 布局。方式三交给系统自动旋转这是最简单的一种把方向设为 AUTO_ROTATION然后不做任何额外处理。系统会依据传感器和页面栈自动旋转。适合没有特殊布局要求的页面省心。三种方式没有严格的好坏之分。如果页面有复杂的自适应布局用方式二监听配置回调做动态适配如果只是偶尔切换页面方向用方式一最直接如果对旋转没有感知需求交给系统自动旋转即可。1. 从旋转到适配一套方案背后的技术取舍很多人一开始的想法很简单既然是横竖屏切换那就在 manifest 里把 screenOrientation 改一下或者调用系统接口旋转窗口完事。但在 HarmonyOS 6 的 Stage 模型下这套思路远远不够。因为窗口旋转只是第一步旋转之后布局自适应、状态保存、页面栈方向同步才是真正的工作量所在。我在实际编码前花了整整一天梳理需求最终拆出了五个子问题子问题核心描述对应方案窗口方向控制如何让窗口旋转到目标方向配置声明 窗口 API 动态设置布局自适应旋转后控件不重叠、不裁剪断点 栅格 自适应布局状态保存与恢复旋转时页面数据不丢状态管理 生命周期处理页面栈方向同步页面跳转时方向正确衔接跳转前设置 页面生命周期恢复性能与体验旋转不卡顿、不过度重建合理使用 onConfigurationUpdated这个表格基本勾勒了完整方案的骨架。接下来每一块都会在正文中展开先给读者一个全局视图后面踩坑时心里有数。1.1 窗口旋转的本质不是旋转屏幕是旋转 UI 坐标系这里需要先纠正一个非常普遍的误解。窗口旋转在鸿蒙系统里本质上是改变 UI 坐标系的朝向让渲染结果跟随屏幕方向输出而不是物理上转动屏幕。因此旋转后布局重新测量、重新排列是必然发生的这正是许多控件在旋转后变样的根本原因。理解这一点以后后面所有的适配策略就顺理成章了既然坐标系变了那布局的所有绝对坐标、尺寸依赖都要重新计算。所以真正难的不是旋转而是让界面在旋转后看起来依然合理。这就是为什么断点设计、栅格布局、自适应布局能力成为横竖屏适配的核心。在 HarmonyOS 6 里ArkUI 为开发者提供了断点Breakpoint和栅格Grid两大布局利器。断点用于感知窗口宽度所处的档位栅格用于把一行划分为若干列按档位决定元素占几列。这两个能力配合横竖屏切换时布局可以自动重排开发量比传统 Android 的多套 layout 要小很多。下面这段伪代码展示了断点的基本使用逻辑// 获取当前窗口宽度判断所在档位 // sm: 320vp - 599vp md: 600vp - 839vp lg: 840vp 以上 const breakpoint this.getBreakpoint(); if (breakpoint sm) { // 手机竖屏下的紧凑布局 } else if (breakpoint md) { // 折叠屏展开 / 手机横屏 } else { // 平板 / 大屏 }这套断点机制非常有用它让旋转后布局变化不再是玄学而是可通过代码精确控制的可预测行为。1.2 为什么必须同时处理窗口级和页面级两个维度横竖屏切换需求在不同业务场景下落地的层级完全不同。有的 App 全局支持旋转任何页面都随设备转有的 App 只允许特定页面横屏比如视频详情页、图表报表页还有的 App 希望在某个页面内部支持手动切换方向比如点击一个按钮从竖屏漫画切换到横屏阅读。这三种场景分别对应三个维度全局配置、页面配置、运行时动态切换。我在设计这套方案时刻意把三个维度分开处理因为它们的代码路径各不相同混在一起很容易出问题。全局维度走的是 WindowStage 和 Configuration页面维度走的是页面生命周期运行时动态切换走的是窗口 API 和状态管理。三者之间既有联系又有边界下面这个表格能直观看出差别维度配置方式适用场景典型代码位置全局module.json5 窗口全局设置全局支持旋转EntryAbility / 入口页面页面页面生命周期设置方向单页面强制横/竖页面 onAppear / onDisappear动态按钮 / 事件触发窗口 API用户手动切换页面内交互逻辑实际编码时每个页面要明确自己属于哪一类是全局支持旋转、强制横屏还是手动切换。这一点想清楚后面的代码结构会非常干净。否则一边在全局开了 auto_rotation一边在某个页面强制横屏两个逻辑互相打架调试时很难查。1.3 方案整体技术选型Router 还是 Navigation在做页面跳转与方向联动时会面临一个路由选型问题。HarmonyOS 里页面跳转有两种主流方式Router和Navigation。两者都能实现跳转但和方向联动结合时的表现差异很大。Router 是老牌路由机制API 简单router.pushUrl、router.back一把梭适合简单场景。但 Router 的页面栈是隐式的方向切换时需要自己用生命周期管理处理不当就容易出现 3.3 里的返回页面方向恢复失败问题。Navigation 是较新的推荐方案基于 NavPathStack 实现每个 NavDestination 都有独立的生命周期方向设置和页面生命周期自然绑定。配合路由动效、转场动画体验和代码结构都更现代。如果是从零开始的新项目我建议优先用 Navigation后续维护省心不少。当然如果项目里已经用 Router 写了大量页面也没必要一次性重构。只需在页面生命周期回调里补上方向恢复逻辑Router 也能跑得很稳。技术选型从来不是非 A 即 B适配自己的存量业务代码才是第一原则。2. 核心细节解析布局与生命周期深水区这一章进入真正动手前必须吃透的细节。第一节把方案和技术选型聊清楚了第二、三节沿着布局自适应和生命周期两条主线展开这两个点恰恰是横竖屏适配的高频踩坑区宁可多花时间看明白也尽量不要边写边试。2.1 布局自适应的三大兵器断点、栅格、自适应能力横竖屏旋转以后窗口的宽高比例会发生变化。竖屏时手机宽度约 360vp横屏时宽度变成约 800vp、高度约 360vp。如果页面里用的是固定宽度控件旋转后必然出现空白、重叠甚至溢出。ArkUI 应对这个问题主要靠三样东西。第一样是断点Breakpoint。系统会把设备宽度划分为 sm320vp-599vp、md600vp-839vp、lg840vp 以上三档。通过MediaQuery或GridRow的断点能力可以让布局在不同档位下呈现不同的结构。这个思路和 Web 里的媒体查询非常像理解了 CSS 响应式再理解 ArkUI 断点几乎是零成本迁移。第二样是栅格Grid。GridRow 和 GridCol 的组合可以把页面划分为 12 列每个内容块根据断点给不同的跨列数。比如竖屏时侧边栏占 4 列、内容占 8 列横屏时侧边栏占 3 列、内容占 9 列。这样旋转后布局自动重排不需要写多套页面。第三样是自适应布局能力。包括拉伸flexGrow、缩放flexShrink、折行flexWrap、隐藏visibility等。组合使用这些能力可以让单个控件在宽窄不同时弹性变化避免出现旋转后按钮挤成一团的惨状。我见过不少团队做横竖屏适配第一版都是堆固定尺寸第二版开始用百分比第三版才意识到需要断点栅格。其实在 HarmonyOS 里断点栅格从第一版就该用因为 Stage 模型下窗口尺寸变化的频率远高于传统手机折叠屏的展开折叠、分屏、悬停都会触发布局变化只有一开始就走自适应路线才不会在后面反复返工。2.2 旋转时的页面生命周期别让状态旋转丢了窗口旋转会触发页面的配置变更回调但并不会销毁重建整个页面。这和传统 Android 在旋转时 Activity 重建的机制不同。对齐到 ArkUI就是onConfigurationUpdated会触发但aboutToAppear/aboutToDisappear不一定会触发。许多开发者第一次做旋转时会踩进这个认知坑以为旋转后页面会重建然后在aboutToAppear里做初始化结果发现旋转后aboutToAppear根本没走。正确处理思路是把依赖初始窗口尺寸的布局代码和不依赖尺寸的初始化代码分开。前者放在配置变更回调或断点变化事件里后者放在aboutToAppear里。这样才能确保旋转后布局重排正确同时初始化逻辑不会重复执行。为了处理状态保存推荐用State配合StorageLink或LocalStorage。页面数据尽量存到状态对象里而不是散落在临时变量中。因为配置变更时页面实例还在状态对象里的数据自然保留。但如果页面真的被销毁了比如系统资源紧张或路由返回状态还在不在就取决于你用的是页面内存状态还是应用级存储。关键数据最好做持久化用 Preferences 存一份至少不会因为旋转问题丢失用户数据。2.3 横竖屏切换对 UI 状态的影响与恢复横竖屏不仅仅影响布局还会影响 UI 状态。最典型的就是弹窗、键盘、焦点位置。横屏时弹出软键盘、竖屏时键盘弹出位置完全不一样页面焦点控件可能因为布局变化而偏移还有 Toast、半模态等浮层旋转后位置也会变化。在 HarmonyOS 6 里处理这类问题需要一个旋转感知的状态管理模型。具体来说有三点建议第一不要在任何状态对象里保存绝对坐标或基于尺寸的偏移量旋转后这些值一律作废要用相对位置或按新窗口尺寸重新计算。第二模态类弹窗展示时优先考虑是否需要在旋转时关闭或重新定位。比如横屏时弹出的底部菜单旋转到竖屏后可能超出屏幕。要么在配置变更回调里关闭弹窗要么把弹窗设计成自适应的浮层。第三列表滚动位置在旋转后会变化如果需要精确定位到某个 item要在配置变更时记录当前 index然后在新布局下重新滚动过去。这个细节容易被忽略但在资讯流、搜索结果页里非常影响体验。把生命周期和状态管理想清楚旋转切换就不再是恐怖故事了。3. 实操代码解析从能转到转得优雅3.1 模块配置与全局方向设置实际操作中在 module.json5 里配置好orientation后还需要配置支持的能力。如果你的应用需要跟随传感器旋转记得检查设备是否支持或者至少在代码层面对不支持旋转的设备做降级处理。配置完以后强烈建议在EntryAbility的onWindowStageCreate里调用一次setPreferredOrientation作为全局默认值。我习惯写成这样import { window } from kit.ArkUI; import { UIAbility } from kit.AbilityKit; export default class EntryAbility extends UIAbility { onWindowStageCreate(windowStage: window.WindowStage): void { const mainWindow windowStage.getMainWindowSync(); mainWindow.setPreferredOrientation(window.Orientation.AUTO_ROTATION); } }这里再补充一个常见疑问为什么配置了 module.json5 还要在代码里再设一次因为 module.json5 的 orientation 是启动方向只在 Ability 冷启动时生效代码里的setPreferredOrientation是运行方向可以在任意时刻覆盖。如果两者不一致以代码实际调用为准。全局统一在代码里设置的好处是后续无论从哪个页面进入方向状态都是可预测的。3.2 页面跳转联动方向Navigation 推荐方案项目使用 Navigation 时推荐在 NavDestination 的生命周期里做方向管理。示例代码如下Entry Component struct Index { navStack: NavPathStack new NavPathStack(); build() { Navigation(this.navStack) { Column() { Button(进入横屏页面) .onClick(() { this.navStack.pushPathByName(detail, null); }) } } } }详情页里在onShown和onHidden里分别设置横竖屏方向Builder export function detailPage() { NavDestination() { // 页面内容 } .onShown(() { const win window.getLastWindow(getContext(this)); win.then((windowObj) { windowObj.setPreferredOrientation(window.Orientation.LANDSCAPE); }); }) .onHidden(() { const win window.getLastWindow(getContext(this)); win.then((windowObj) { windowObj.setPreferredOrientation(window.Orientation.PORTRAIT); }); }) }这样页面显示时自动横屏离开时自动恢复竖屏整个跳转链路非常干净。Navigation 的方案里NavDestination 的 onShown / onHidden 就是天然的进入页面/离开页面信号方向逻辑放在这里最合适。3.3 旋转感知的自适应布局代码布局是旋转适配的重头戏。下面给出一个基于 GridRow 的示例演示如何用断点控制横竖屏下的列分布。Entry Component struct GridPage { State currentBreakpoint: string sm; build() { GridRow({ columns: 12, gutter: { x: 12, y: 12 }, breakpoints: { value: [400vp, 600vp, 840vp], reference: BreakpointsReference.Window } }) { GridCol({ span: { sm: 12, md: 8, lg: 6 } }) { // 主内容区 } GridCol({ span: { sm: 12, md: 4, lg: 6 } }) { // 侧边栏 / 辅助区 } } .onBreakpointChange((breakpoint: string) { this.currentBreakpoint breakpoint; }) } }这段代码的含义是窗口宽度小于 400vp 时算 sm 档主内容区和辅助区各占 12 列上下堆叠400vp-600vp 之间算 md 档主内容占 8 列、辅助区占 4 列左右排布超过 600vp 算 lg 档两边各占 6 列。旋转到横屏后断点自动进入 md 或 lg 档布局自动变成左右分栏完全不需要手写两个页面。这里注意BreakpointsReference.Window表示断点参照窗口宽度如果参照容器宽度行为会有一点点差异。绝大多数页面场景下用 Window 参照即可。3.4 横竖屏切换的时序与动态更新横竖屏切换发生时onBreakpointChange和onConfigurationUpdated是有先后时序的。就经验来看onConfigurationUpdated更早被触发之后是断点变化回调。因此如果需要在旋转后读取新布局、刷新数据放在哪个回调里直接影响效果。我建议遵循一条原则断点变化回调只做 UI 调整配置更新回调做数据刷新。如果在配置更新里操作 DOM/UI 组件有时会拿到旧尺寸而在断点变化回调里数据没刷新又拿不到新数据。两者分工明确代码逻辑才不会互相干扰。下面是一个简单的动态更新示例onConfigurationUpdated(newConfig: Configuration): void { // 数据层刷新 const direction newConfig.direction; if (direction ! this.lastDirection) { this.refreshDataByDirection(direction); } } Builder buildGridContent() { // UI 层根据 currentBreakpoint 控制布局 }旋转后数据层刷新、UI 层重排配合 2.2 的状态保留方案就能做到转得又快又稳。2. 生命周期与状态管理2.1 窗口尺寸变化对状态的影响窗口尺寸变化除了影响布局还会影响页面内所有用到宽高的逻辑。比如进度条、动画位移、滚动高度、甚至是计算文本行数。如果这些逻辑的依赖值是启动时读取一次旋转后就会出错。我的经验是所有依赖窗口尺寸的变量都应该是响应式的。要么放在State里并在配置更新时重新赋值要么直接用onSizeChange获取组件实时尺寸。用State配合生命周期回调是在 ArkUI 里处理这类问题最通用的模式。另一个隐藏问题是安全区。横屏后设备刘海区域的位置和大小可能变化如果页面里有沉浸式设计需要动态调整顶部和底部的 padding。HarmonyOS 提供了expandSafeArea和setWindowLayoutFullScreen等接口旋转后安全区变化事件也会触发记得绑定监听。2.2 状态持久化与页面重建的边界回到状态管理我踩过一次比较典型的坑页面设置了横屏方向旋转过程中aboutToDisappear触发、然后页面被重建重建后方向又变回了竖屏。排查下来发现页面重建时 module.json5 里配置的方向是竖屏而代码里动态设置的方向没有在重建后重新执行。解决这个问题的方法是把方向状态存到应用级 LocalStorage 或 Preferences 里。页面重建后读取存储里的方向再调用窗口 API 恢复。简单说方向状态不能只在内存里重要页面要把方向偏好持久化。关于本地存储的代码很简单但三言两语说不完。建议在项目里引入一个统一的状态管理模块把横竖屏、旋转锁定、亮度、音量这类系统状态都管起来页面只做状态读写不直接操作窗口 API。这也是我在后续项目中保持代码整洁的重要原因。2. 常见问题与实战排查那些年我踩过的坑2.1 旋转后页面布局错乱这是遇到最多的问题。原因通常是页面布局没有采用自适应方案或者虽然用了断点但断点档位设置得不合理。排查方法也很直接先在 DevEco Studio 的预览器里手动切换设备方向看布局在哪个档位开始异常然后针对档位调整布局参数。要特别留意的是固定高度的坑。很多开发者习惯给顶部栏和底部栏设置固定高度竖屏时没问题横屏后顶部栏和底部栏占满太多空间中间内容区被压缩得无法使用。解决方案是把顶部栏高度改为fit或根据状态栏高度动态计算。2.2 页面方向恢复失败跳转到横屏页面后返回原页面方向没有恢复成竖屏。这个问题的根源和 3.3 里说的一致方向设置没有绑定页面自身的生命周期。排查时先确认代码里有没有在 onDisappear/onHidden 里恢复方向再确认恢复的方向值是否正确。如果还是不行检查是否有其他地方比如 EntryAbility 的全局设置覆盖了你恢复的方向。2.3 旋转时页面数据丢失现象是旋转后列表回到顶部、表单输入内容被清空。原因多半是状态没有正确管理。要么页面在旋转时被重建要么布局变化导致列表滚动位置丢失。解决方案和 2.2 节一致数据存入状态对象滚动位置记录并在配置更新后恢复。如果页面被重建不可避免一定要把关键数据持久化否则没有魔法可以恢复。2.4 子窗口弹窗、输入法方向异常子窗口的典型问题有两个一个是弹窗在旋转后位置错位另一个是输入法面板在横屏时盖住输入框。处理弹窗我建议在onConfigurationUpdated里关闭当前弹窗并重新弹出位置会用新窗口尺寸计算输入法的处理是在横屏时调整输入框的位置比如抬高到底部安全区之上或者用 scroll 滚动让输入框保持可见。为了节省大家排查时间我把高频问题整理成一个速查表常见问题可能原因排查与解决旋转后布局错乱未使用自适应布局固定尺寸干扰采用断点 栅格检查档位设置返回页面方向未恢复方向设置未绑定生命周期在 onDisappear / onHidden 恢复方向旋转后数据丢失状态未持久化滚动位置未记录关键状态存入 LocalStorage / Preferences弹窗位置错位弹窗坐标未自动适配旋转时关闭弹窗并按新窗口重新弹出输入法遮挡输入框未处理横屏安全区动态调整输入框位置配合滚动页面重建方向丢失方向依赖内存状态方向偏好持久化重建后再恢复断点切换不生效断点值设得过于接近窗口宽度调整断点阈值留足裕量6. 踩坑与经验总结最后分享几条这几年做鸿蒙适配积累下来的实在经验都是真金白银换来的教训。第一横竖屏适配永远不要放到最后再做。项目一开始就建立断点机制和状态管理规范后面加页面只是套模板如果等到页面全部写完再回头适配所有固定尺寸代码都会变成债越改越痛苦。第二测试横竖屏一定要在真机上进行。模拟器的方向切换和真机传感器行为差异很大尤其是连接蓝牙设备、口罩模式、折叠屏展开这些场景模拟器根本模拟不出来。很多诡异问题只在真机上复现。第三旋转切换时尽量减少 UI 重建。别一旋转就把整个页面销毁重建尽量复用已有组件树只在布局尺寸和方向上做增量更新。ArkUI 的重渲染成本虽然低但页面一旦复杂起来过度重建照样会掉帧。第四方向状态和业务状态分离。不要让页面里的业务逻辑依赖方向状态方向只影响布局和视觉呈现不影响数据流。业务逻辑保持单向数据流方向状态作为 UI 状态注入这种设计在调试时极其舒适。如果你按照这个思路把方案落地不仅能解决当前的横竖屏切换需求还能顺手把页面自适应能力做了折叠屏、分屏、多窗口切换都会变得从容许多。HarmonyOS 6 的窗口体系确实有自己的一套逻辑但当你真正写下来会发现它比想象中要顺手得多。我个人在实际编码里的体会是横竖屏切换这个功能难度不在于调用一个旋转 API而在于把旋转当作一个跨页面、跨状态、跨布局的系统性变化来看待。谁能在设计阶段想清楚这三点谁就能在开发阶段少熬夜。